Railway Operation Simulator  v2.10.0
A railway simulator for Windows
TrainUnit.cpp
Go to the documentation of this file.
1 // TrainUnit.cpp
2 /*
3  BEWARE OF COMMENTS in .cpp files: they were accurate when written but have
4  sometimes been overtaken by changes and not updated
5  Comments in .h files are believed to be accurate and up to date
6 
7  This is a source code file for "railway.exe", a railway operation
8  simulator, written originally in Borland C++ Builder 4 Professional with
9  later updates in Embarcadero C++Builder 10.2.
10  Copyright (C) 2010 Albert Ball [original development]
11 
12  This program is free software: you can redistribute it and/or modify
13  it under the terms of the GNU General Public License as published by
14  the Free Software Foundation, either version 3 of the License, or
15  (at your option) any later version.
16 
17  This program is distributed in the hope that it will be useful,
18  but WITHOUT ANY WARRANTY; without even the implied warranty of
19  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20  GNU General Public License for more details.
21 
22  You should have received a copy of the GNU General Public License
23  along with this program. If not, see <http://www.gnu.org/licenses/>.
24 */
25 // ---------------------------------------------------------------------------
26 #include <Classes.hpp>
27 #include <Controls.hpp>
28 #include <StdCtrls.hpp>
29 #include <Forms.hpp>
30 #include <Buttons.hpp>
31 #include <ExtCtrls.hpp>
32 #include <Menus.hpp>
33 #include <Dialogs.hpp>
34 #include <Graphics.hpp>
35 #include <ComCtrls.hpp>
36 #include <fstream>
37 #include <vector>
38 #include <algorithm> //for sort
39 #include <vcl.h>
40 #include <stdlib.h> //for rand()
41 #include <math.hpp> //for speed & performance calcs
42 
43 #pragma hdrstop
44 
45 #include "TrainUnit.h"
46 #include "TrackUnit.h"
47 #include "GraphicUnit.h"
48 #include "DisplayUnit.h"
49 #include "Utilities.h"
50 
51 // ---------------------------------------------------------------------------
52 #pragma package(smart_init)
53 
55 
56 // ---------------------------------------------------------------------------
57 
58 int TTrain::NextTrainID = 0; // has to be initialised outside the class
59 
60 // ---------------------------------------------------------------------------
61 
62 TTrain::TTrain(int Caller, int RearStartElementIn, int RearStartExitPosIn, AnsiString InputCode, int StartSpeedIn, int MassIn, double MaxRunningSpeedIn,
63  double MaxBrakeRateIn, double PowerAtRailIn, TTrainMode TrainModeIn, TTrainDataEntry *TrainDataEntryPtrIn, int RepeatNumberIn, int IncrementalMinutesIn,
64  int IncrementalDigitsIn, int SignallerMaxSpeedIn) : RearStartElement(RearStartElementIn), RearStartExitPos(RearStartExitPosIn), HeadCode(InputCode),
65  StartSpeed(StartSpeedIn), Mass(MassIn), MaxRunningSpeed(MaxRunningSpeedIn), MaxBrakeRate(MaxBrakeRateIn), PowerAtRail(PowerAtRailIn),
66  TrainMode(TrainModeIn), TrainDataEntryPtr(TrainDataEntryPtrIn), RepeatNumber(RepeatNumberIn), IncrementalMinutes(IncrementalMinutesIn),
67  IncrementalDigits(IncrementalDigitsIn), SignallerMaxSpeed(SignallerMaxSpeedIn)
68 /*
69  Construct a new train with general default values and input values for position and headcode.
70  Create the frontcode, headcode and background graphics here but don't delete them in a destructor.
71  This is because trains are kept in a vector and vectors erase elements during internal operations.
72  Deletion is explicit by using a special function. Increment the static class member NextTrainID
73  after setting this train's ID.
74 */
75 
76 {
77  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",TTrain," + AnsiString(RearStartElementIn) + "," +
78  AnsiString(RearStartExitPosIn) + "," + AnsiString(InputCode) + "," + AnsiString(StartSpeedIn) + "," + AnsiString(MassIn) + "," +
79  AnsiString(TrainModeIn));
80  // AutoControl = true;//all trains start in auto control
81  UpdateCounter = 0;
82  TimeTimeLocArrived = false;
83  Derailed = false;
84  DerailPending = false;
85  Crashed = false;
86  StoppedAtBuffers = false;
87  StoppedAtSignal = false;
88  StoppedAtLocation = false;
89  StoppedAfterSPAD = false;
90  StoppedWithoutPower = false; // new at v2.4.0
91  StoppedForTrainInFront = false;
92  SignallerStoppingFlag = false;
93  SignallerStopped = false;
94  SignallerRemoved = false;
95  NotInService = false;
96  HoldAtLocationInTTMode = false;
97  AllowedToPassRedSignal = false;
98  CallingOnFlag = false;
99  BeingCalledOn = false;
100  DepartureTimeSet = false;
102  TimetableFinished = false;
103  LastActionDelayFlag = false;
104  OneLengthAccelDecel = false;
105  TrainCrashedInto = -1;
107  Plotted = false;
108  TrainGone = false;
109  SPADFlag = false;
110  FrontCodePtr = new Graphics::TBitmap;
111  FrontCodePtr->PixelFormat = pf8bit;
112  FrontCodePtr->Height = 8;
113  FrontCodePtr->Width = 8;
115  FrontCodePtr->Transparent = false;
116  AValue = sqrt(2 * PowerAtRail / Mass);
118  TerminatedMessageSent = false;
119  JoinedOtherTrainFlag = false;
121  StepForwardFlag = false;
123  for(int x = 0; x < 4; x++)
124  {
125  HeadCodeGrPtr[x] = new Graphics::TBitmap;
126  HeadCodeGrPtr[x]->PixelFormat = pf8bit;
127  HeadCodeGrPtr[x]->Height = 8;
128  HeadCodeGrPtr[x]->Width = 8;
130  HeadCodeGrPtr[x]->Transparent = false;
131  }
132  for(int x = 0; x < 4; x++)
133  {
134  BackgroundPtr[x] = new Graphics::TBitmap;
135  BackgroundPtr[x]->PixelFormat = pf8bit;
136  BackgroundPtr[x]->Height = 8;
137  BackgroundPtr[x]->Width = 8;
139  BackgroundPtr[x]->Transparent = false;
140  }
141  for(int x = 0; x < 4; x++)
142  {
144  // set here to ensure have values
145  }
146  for(int x = 0; x < 4; x++)
147  {
148  PlotElement[x] = -1; // marker for not plotted yet
149  }
150  for(int x = 0; x < 3; x++)
151  {
152  OldZoomOutElement[x] = -1; // marker for not plotted yet
153  }
155  NextTrainID++;
156 
157  // new values added to complete initialisation of all TTrain variables
158 
159  // ActionVectorEntryPtr = &(TrainDataEntryPtr->ActionVector.at(0)); can't be initialised yet as session trains created with Null
160  // TrainDataEntryPtr, initialise in AddTrain
162  FrontElementLength = 0;
163  EntrySpeed = 0;
164  ExitSpeedHalf = 0;
165  ExitSpeedFull = 0;
166  MaxExitSpeed = 0;
167  BrakeRate = 0;
169  FirstHalfMove = true;
170  EntryTime = 0;
171  ExitTimeHalf = 0;
172  ExitTimeFull = 0;
173  ReleaseTime = 0;
174  TRSTime = 0;
175  LastActionTime = 0;
176  Straddle = MidLag;
177  LeadElement = -1;
178  LeadEntryPos = 0;
179  LeadExitPos = 0;
180  MidElement = -1;
181  MidEntryPos = 0;
182  MidExitPos = 0;
183  LagElement = -1;
184  LagEntryPos = 0;
185  LagExitPos = 0;
186  TrainFailed = false; // added at v2.4.0
187  for(int x = 0; x < 4; x++)
188  {
189  HOffset[x] = 0;
190  VOffset[x] = 0;
191  PlotEntryPos[x] = 0;
192  }
193  OpTimeToAct = 60; // default value, new at v2.2.0
194  MinsDelayed = 0.0; // new at v2.2.0
195  FirstLaterStopRecoverableTime = 0.0; // new at v2.2.0
196  FinishJoinLogSent = false;
197  // added at v2.4.0 to prevent repeatdly logging the event
200  // added at v2.4.0, no need to include in session file as will only be sent once & better that way
204  ZeroPowerNoCDTMessage = false;
209  TrainFailurePending = false;
210  Utilities->CallLogPop(648);
211 }
212 
213 // ---------------------------------------------------------------------------
214 
215 void TTrain::DeleteTrain(int Caller)
216 /*
217  Delete train heap objects (bitmaps) explicitly by this special function rather than by a destructor, because vectors
218  erase elements during internal operations & if TTrain had an explicit destructor that deleted the heap elements then
219  it would be called when a vector element was erased. Calling the default TTrain destructor doesn't matter because all that
220  does is release the memory of the members (including pointers to the bitmaps), it doesn't destroy the bitmaps themselves.
221  It's important therefore to call this function before erasing the vector element, otherwise the pointers to the bitmaps
222  would be lost and the bitmaps never destroyed, thereby causing memory leaks.
223  No need to delete HeadCodePosition as that just points to existing bitmaps
224 */{
225  // if(NoDelete) return;//used when a TTrain is created to hold copied values from elsewhere
226  TrainController->LogEvent("" + AnsiString(Caller) + ",DeleteTrain," + HeadCode);
227  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",DeleteTrain," + HeadCode);
228  if(Display->ZoomOutFlag)
229  {
231  }
232  if(FrontCodePtr == 0)
233  {
234  throw Exception("Error in attempting to delete FrontCodePtr");
235  }
236  delete FrontCodePtr;
237  FrontCodePtr = 0;
238  for(int x = 0; x < 4; x++)
239  {
240  if(BackgroundPtr[x] == 0)
241  {
242  throw Exception("Error in attempting to delete BackgroundPtr[" + AnsiString(x) + "]");
243  }
244  delete BackgroundPtr[x];
245  BackgroundPtr[x] = 0;
246  }
247  for(int x = 0; x < 4; x++)
248  {
249  if(HeadCodeGrPtr[x] == 0)
250  {
251  throw Exception("Error in attempting to delete HeadCodeGrPtr[" + AnsiString(x) + "]");
252  }
253  delete HeadCodeGrPtr[x];
254  HeadCodeGrPtr[x] = 0;
255  }
256  Utilities->CallLogPop(649);
257 }
258 
259 // ---------------------------------------------------------------------------
260 
262 /*
263  Plots the train starting position on screen. Note that the check for starting on straight points &
264  on wrongly set points is carried out in TrainControllerUnit [but have to allow for starting on points because
265  ChangeDirection calls this function.]. Train starts on Lead & Mid elements & Straddle = LeadMid unless
266  entering at a continuation in which case Straddle = MidLag & train not plotted immediately.
267  Set the headcode graphics pointers from the headcode text, then check whether starting at a
268  continuation. If so set Mid & Lag elements to -1 so they won't be plotted, and set Lead values
269  for the continuation element. Otherwise set Lead and Mid values,
270 
271  and Lead element value unless
272  Mid element is a buffer or continuation. Set Straddle, then for the Mid element set the graphic
273  offsets and headcode positions and front code. Pick up background bitmaps for the Mid element,
274  then check if a train on either Mid or Lag and if so give a warning message and return false so
275  that the calling function can delete the train. Plot the Mid element train values then do similarly
276  for the Lag element - set offsets, pick up background bitmaps, and plot the rear two segments of
277  the train. Finally set the Plotted flag and return true.
278 */{
279  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",PlotStartPosition," + HeadCode);
280  int NextElementPosition, NextEntryPos, ElementLength, SpeedLimit;
281 
283  // PlotStartTime = TrainController->TTClockTime;
284  FirstHalfMove = true;
285 
286  // if enter at continuation then don't plot anything at start, but set TrainIDOnElement for continuation entry so as to
287  // 'claim' it for this train to prevent any other waiting trains trying to enter
289  {
290  LagElement = -1; // not to be plotted
291  LagExitPos = 0; // not to be plotted
292  LagEntryPos = 0; // not to be plotted
293  MidElement = -1; // not to be plotted
294  MidExitPos = 0; // not to be plotted
295  MidEntryPos = 0; // not to be plotted
297  LeadExitPos = 1; // will be 1 for continuation entry
298  LeadEntryPos = 0;
299 
301  MaxExitSpeed = StartSpeed; // initial value
303  ElementLength = Track->TrackElementAt(164, LeadElement).Length01;
304  SpeedLimit = Track->TrackElementAt(165, LeadElement).SpeedLimit01;
305  if(EntrySpeed > SpeedLimit)
306  {
307  EntrySpeed = SpeedLimit;
308  }
310  {
312  }
314  // LeadElement is the element to be entered
315 
316  // Precautionary check - If need to brake EntrySpeed may be too high, so set it to the speed at which
317  // can achieve ExitSpeedFull at the half braking rate.
319  {
320  double TempEntrySpeed = sqrt((MaxExitSpeed * MaxExitSpeed) + (3.6 * 3.6 * MaxBrakeRate * ElementLength)); // half braking
321  if(TempEntrySpeed < EntrySpeed)
322  {
323  EntrySpeed = TempEntrySpeed;
325  }
326  }
327  Straddle = MidLag; // only for starting on a continuation
329  // no need to stop gap flashing if start on continuation
330  }
331  else // not starting at a continuation
332  {
333  LagElement = -1;
334  LagEntryPos = 0;
335  LagExitPos = 0;
342 
344  MaxExitSpeed = StartSpeed; // initial value
346  bool TempDerail = false; // dummy
347  NextElementPosition = Track->TrackElementAt(168, LeadElement).Conn[Track->GetAnyElementOppositeLinkPos(2, LeadElement, LeadEntryPos, TempDerail)];
349  if((PowerAtRail < 1) && EntrySpeed < 1) // added at v2.4.0
350  {
351  StoppedWithoutPower = true;
352  }
353  // facing buffers check - ignore starting speed if start facing buffers
354  StoppedAtBuffers = false;
355  // need to set here as well as in UpdateTrain() in case paused during signaller change direction
358  {
359  FrontElementSpeedLimit = Track->TrackElementAt(494, LeadElement).SpeedLimit01; // use 01 for convenience, not used
360  FrontElementLength = Track->TrackElementAt(495, LeadElement).Length01; // use 01 for convenience, not used
361  EntrySpeed = 0;
362  ExitSpeedHalf = 0;
363  ExitSpeedFull = 0;
364  MaxExitSpeed = 0;
365  // SetTrainMovementValues not called so set this here
366  BrakeRate = 0;
369  StoppedAtSignal = false;
370  // new v2.2.0: can't be at buffers and signal! If was set then won't be reset as later
371  // signal check is an 'else'
372  if(!StoppedAtLocation)
373  {
374  StoppedAtBuffers = true; // stopped at location takes precedence
375  }
376  }
377 
378  // facing continuation check - don't allow to stop even if no power
380  {
381  FrontElementSpeedLimit = Track->TrackElementAt(509, LeadElement).SpeedLimit01; // use 01 for convenience, not used
382  FrontElementLength = Track->TrackElementAt(510, LeadElement).Length01; // use 01 for convenience, not used
386  BrakeRate = 0;
387  ExitTimeHalf = TrainController->TTClockTime + TDateTime(1.8 * (double) FrontElementLength / EntrySpeed / 86400);
388  ExitTimeFull = TrainController->TTClockTime + TDateTime(3.6 * (double) FrontElementLength / EntrySpeed / 86400);
389  }
390 
391  // Signal check
392  else if((NextElementPosition > -1) && (NextEntryPos > -1))
393  // condition check added as precaution after SloughIECC error reported by James U
394  {
395  if((Track->TrackElementAt(170, NextElementPosition).Config[Track->GetNonPointsOppositeLinkPos(NextEntryPos)] == Signal) &&
396  (Track->TrackElementAt(171, NextElementPosition).Attribute == 0) && !StoppedWithoutPower)
397  {
398  FrontElementSpeedLimit = Track->TrackElementAt(172, LeadElement).SpeedLimit01; // use 01 for convenience, not used
399  FrontElementLength = Track->TrackElementAt(173, LeadElement).Length01; // use 01 for convenience, not used
400  EntrySpeed = 0;
401  ExitSpeedHalf = 0;
402  ExitSpeedFull = 0;
403  MaxExitSpeed = 0;
404  BrakeRate = 0;
407  if(!StoppedAtLocation) //if it is stopped at location then don't want StoppedAtSignal until departure time if still red then, & UpdateTrain takes care of thet
408  {
409  StoppedAtSignal = true;
411  // TrainController->LogActionError(39, HeadCode, "", SignalHold, Track->TrackElementAt(754, NextElementPosition).ElementID);
412  }
414  {
415  // set both StoppedAtLocation & StoppedAtSignal, so that 'pass red signal' is offered in popup menu rather than move
416  // forwards, but don't change the background colour so still shows as stopped at location
417  StoppedAtSignal = true;
418  }
419  }
420  else
421  {
422  StoppedAtSignal = false;
423  if(NextEntryPos > 1)
424  {
425  ElementLength = Track->TrackElementAt(174, NextElementPosition).Length23;
426  SpeedLimit = Track->TrackElementAt(175, NextElementPosition).SpeedLimit23;
427  }
428  else
429  {
430  ElementLength = Track->TrackElementAt(176, NextElementPosition).Length01;
431  SpeedLimit = Track->TrackElementAt(177, NextElementPosition).SpeedLimit01;
432  }
433  if(EntrySpeed > SpeedLimit)
434  {
435  EntrySpeed = SpeedLimit;
436  }
438  {
440  }
442  TDateTime TestTime = TrainController->TTClockTime; // test
443  AnsiString TimeString = Utilities->Format96HHMMSS(TestTime); // test
444  SetTrainMovementValues(2, NextElementPosition, NextEntryPos);
445  // NextElement is the element to be entered
446 
447  // Precautionary check - If need to brake EntrySpeed may be too high, so set it to the speed at which
448  // can achieve ExitSpeedFull at the half braking rate.
450  {
451  double TempEntrySpeed = sqrt((MaxExitSpeed * MaxExitSpeed) + (3.6 * 3.6 * MaxBrakeRate * ElementLength));
452  // half braking
453  if(TempEntrySpeed < EntrySpeed)
454  {
455  EntrySpeed = TempEntrySpeed;
456  SetTrainMovementValues(3, NextElementPosition, NextEntryPos);
457  }
458  }
459  }
460  }
462  {
463  throw Exception("Error, LeadElement Exit Connection is NotSet");
464  }
465  }
466  if(MidElement > -1) // will be -1 if start on continuation
467  {
468  Straddle = LeadMid;
472  {
473  for(int x = 0; x < 4; x++)
474  {
475  HeadCodePosition[x] = HeadCodeGrPtr[3 - x];
476  }
477  }
478  else
479  {
480  for(int x = 0; x < 4; x++)
481  {
483  }
484  }
485  if(TrainMode == Timetable)
486  {
488  }
489  else
490  {
492  }
494  // pick up background bitmaps [0] & [1] & plot HeadCodes [0] & [1]
495 
498 /* Move check to AddTrain, also, now that can start on bridges need to check that other train is on same track before refusing
499  if((Track->TrackElementAt(182, LeadElement).TrainIDOnElement > -1) || ((MidElement > -1) && (Track->TrackElementAt(183, MidElement).TrainIDOnElement > -1)))
500  {
501  ShowMessage("Can't place train " + HeadCode + "; another train already present at location");
502  Utilities->CallLogPop(651);
503  return false;
504  }
505 */
510  PlotTrainGraphic(8, 0, Display);
511  PlotTrainGraphic(9, 1, Display);
512 
515 
516  // pick up background bitmaps [2] & [3]
517 
520 
521  PlotElement[2] = MidElement;
523  PlotElement[3] = MidElement;
525  PlotTrainGraphic(10, 2, Display);
526  PlotTrainGraphic(11, 3, Display);
527  // Plotted = true; set in PlotTrainGraphic
528  }
529  Display->Update(); // resurrected when Update() dropped from PlotOutput etc
530  Utilities->CallLogPop(652);
531 }
532 
533 // ---------------------------------------------------------------------------
534 void TTrain::UnplotTrain(int Caller)
535 {
536  // Note: If trouble is experienced with the PlotAlternativeTrackRouteGraphic functions remove them & test for train on a bridge and if so call Clearand..
537  if(!Plotted)
538  {
539  return;
540  }
541  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",UnplotTrain," + HeadCode);
542 
543  if(Straddle == MidLag)
544  {
545  if(MidElement > -1)
546  {
551  // to force plot of locked route marker, needed once only for the element
552  }
553  if(LagElement > -1)
554  {
559  // to force plot of locked route marker, needed once only for the element
560  }
561  }
562  else if(Straddle == LeadMidLag)
563  {
564  if(LeadElement > -1)
565  {
568  // to force plot of locked route marker, needed once only for the element
569  }
570  if(MidElement > -1)
571  {
576  // to force plot of locked route marker, needed once only for the element
577  }
578  if(LagElement > -1)
579  {
582  // to force plot of locked route marker, needed once only for the element
583  }
584  }
585  else if(Straddle == LeadMid)
586  {
587  if(LeadElement > -1)
588  {
593  // to force plot of locked route marker, needed once only for the element
594  }
595  if(MidElement > -1)
596  {
601  // to force plot of locked route marker, needed once only for the element
602  }
603  }
604  if(LeadElement > -1)
605  {
607  }
608  if(MidElement > -1)
609  {
611  }
612  if(LagElement > -1)
613  {
615  }
616  Plotted = false;
618  Display->Update();
619  // without this the screen 'blinks' at next Clearand... prob forces a full repaint for some reason
620  // resurrected when Update() dropped from PlotOutput etc
621  Utilities->CallLogPop(653);
622 }
623 
624 // ----------------------------------------------------------------------------
625 
626 void TTrain::UpdateTrain(int Caller)
627 /*
628  Note: Some changes made since comments written
629 
630  Brief:
631  Enter with Straddle defining train position wrt Lag, Mid & Lead elements. Is only MidLag at this point
632  on first entry at a continuation (with no train plotted), in all other cases it is either LeadMid (when train fully
633  on Lead & Mid elements) or LeadMidLag (when train straddling 3 elements).
634  Thereafter on entry Straddle = LeadMidLag or LeadMid; LeadMid if train fully on Mid & Lead elements, and
635  LeadMidLag if on Lag, Mid and Lead elements (back on lag, front on Lead, & middle 2 segments on Mid).
636  If enter with Straddle = LeadMid, then train is in effect in the first half of the next element, and moves half onto it after
637  the half time point has been passed. The values for the next element were set when the train was last updated when Straddle became
638  LeadMid from LeadMidLag. After the half time point has been passed Straddle is
639  changed to MidLag within the function and all elements moved down one, old Mid becomes
640  the new lag, old Lead becomes the new Mid, and a new Lead is obtained. Then the new positions are plotted, and finally Straddle is
641  incremented to reflect the position the train now occupies.
642 
643  Detail:
644  Set TrainFailurePending if all conditions met
645  Check whether stopped at a non-red signal, and if so reset StoppedAtSignal so train can move.
646  Check whether buffers at immediate exit, either when first enter the function or later, and set StoppedAtBuffers if so
647  and return.
648  If Straddle == LeadMid then train fully on Lead and Mid, so ready for a major update:-
649  If there's a LagElement (there will be but include check for good practice - next
650  function depends on it) Check whether DerailPending set - set during last GetLeadElement if appropriate but only acted on here when
651  train fully on offending point - Derail set and DerailPanding reset, train background
652  colour changed (note that BackgroundColour is a property of the train itself) then return.
653  If no derail pending reset Lag and Mid elements to the old Mid and Lead values, reset Straddle to MidLag, then set
654  the new LeadElement, which will be the next connected element (obtain using GetLeadElement) or -1 if the current
655  LeadElement is an exit continuation. During GetLeadElement the element at LeadElement is checked and if a stop
656  signal is found StoppedAtSignal is set to true, otherwise StoppedAtSignal is set to false. Also Derail is set
657  if LeadElement is a fouled trailing point.
658  Now, the train is moved on by one segment. Firstly the last BackgroundElement is set to LagElement, then the last
659  segment of the LagElement is unplotted (if there is a LagElement - may be entering at a continuation), by
660  replotting the last background segment and checking whether the element is a bridge or crossover with the other
661  track in a route, in which case the route colour is replotted.
662  Then, if Straddle == LeadMidLag (train will move completely off the element during this function), and the train
663  track is in a route, then all the train elements are removed from the route unless it's an autosig route. Normally only the
664  LeadElement will be in a route for a moving train, but when originally placed all elements may be in the route so check them all.
665  Note also that there may be two routes at a given element position, but only one of them is the correct one, so this
666  is identified prior to the removal. Also the TrainIDs are reset because the train will be fully off this element at the end of
667  the function. If Straddle == LeadMidlag and the element being left is a ContinuationExit the the TrainGone flag is set so the
668  train can be deleted by the calling function, and the function returns.
669  If the element is a signal in the train movement direction, then it is reset to red (Attribute = 0) and is replotted
670  to show the red aspect. Finally if element is a signal in the other direction it is replotted as it was - need to
671  plot individually because could have any aspect, the background bitmap that was picked up earlier contains just the
672  basic red aspect.
673 
674  Now all the array values are updated, but the [0] values are as yet invalid, these have to be obtained explicitly from
675  the new LeadElement later. The headcode graphics are updated so that it reads correctly - left to right & top to bottom,
676  regardless of direction, and with the correct front code colour.
677 
678  The new front segment background bitmap is now picked up and the graphic offsets set, and the segments are plotted.
679  No more unplotting is needed as all but the last segment are overwritten by later segments, and the new front
680  segment is just plotted, though the background bitmap at that location has to be picked up. Just where they are
681  plotted depends on the Straddle value, [0] is always on Lead, [1] is on Lead if Straddle == LeadMidLag or Mid if
682  Straddle == MidLag; [2] is always on Mid, and [3] is on Mid if Straddle == LeadMidLag or Lag if Straddle == MidLag.
683  Also prior to plotting the lead segment a crash check is made, and if true the Crashed flag is set and the
684  TrainCrashedInto value also set to the current TrainID - this is so it too becomes crashed and hence stopped.
685 
686  The Crashed flag is now checked, and if set the front headcode colour is changed to the same as the rest of the code,
687  and the background colour changed. Then the train that is crashed into is also set to Crashed, and its colours
688  changed similarly. The function then returns.
689 
690  If Crashed is not set then Straddle is incremented and the function returns.
691 */
692 
693 {
694  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",UpdateTrain," + HeadCode);
695  UpdateCounter++;
696  // 100 counts = 5secs (used in splits to prevent too frequent length checks in front & rear splits)
697  if(UpdateCounter >= 100)
698  {
699  UpdateCounter = 0;
700  }
701  int RandRange = (TrainController->MTBFHours * 3600) / 53;
702 
703  // MTBFHours is in timetable clock hours, min value is 1 & max value is 10,000 (integer values on input)
704  // but double on use because it represents timetable clock time, so at 1/16 speed RandRange is * 16 (160,000 max) & at 16x speed its /16 (1/16 min)
705  // i.e MTBFHours is Input value/TTClockSpeed (conversion is done in InterfaceUnit)
706  if(int(TrainController->RandomFailureCounter) == (rand() % 1060))
707  // RandomFailureCounter value is fixed for a full cycle of train updates so this
708  // makes sure there's no bunching of failures as there is for a fixed comparison number
709  // or a small range of comparison numbers. True every 53 secs (real time) on average rand()
710  // gives a random number between 0 and 32767 (defined as RAND_MAX in stdlib.h)
711  {
712  if(!TrainFailed && !TrainOnContinuation(0) && (RandRange > 0) && (PowerAtRail > 1) && !((TrainMode == Timetable) && TimetableFinished)
713  && !Crashed && !Derailed && !((TrainMode == Signaller) && Stopped()))
714  // RandomFailureCounter resets to 0 every 53 secs, if RandRange is 0 then no failure rate is set - i.e. failure rate = 0
715  // don't fail if:
716  // (a) on a continuation (entering or leaving);
717  // (b) already failed;
718  // (c) power effectively zero (8000) min value for powered; 0.08 for 'no power';
719  // (d) train terminated;
720  // (e) crashed or derailed; or
721  // (f) under signaller control and stopped.
722  {
723  if((random(RandRange)) == 0)
724  // max value for RandRange is over 2x10^9
725  {
726  // here if failure due
727  TrainFailurePending = true;
728  // fail when PlotElements set to proper Lead & Mid Elements
729  }
730  }
731  }
732 /* dropped as it allows a train to stop on a half element - when reach (if Stopped()) at line 1310
733  if ((PowerAtRail < 1) && (EntrySpeed < 1)) // added at v2.4.0
734  {
735  StoppedWithoutPower = true;
736  }
737 */
738  int LockedVectorNumber;
739  Graphics::TBitmap *EXGraphicPtr = RailGraphics->bmTransparentBgnd;
740  // default values - these needed for route checker below
741  Graphics::TBitmap *EntryDirectionGraphicPtr = RailGraphics->bmTransparentBgnd;
742 
744  {
746  }
747  if(Crashed || Derailed)
748  {
750  {
751  PlotTrain(7, Display);
752  // replotted every cycle because of level crossing crashes, otherwise a flashing level crossing wipes out half of the train
753  Display->Update();
754  }
755  OpTimeToAct = 0.0;
756  // need to set this here as wouldn't be calculated otherwise as return from UpdateTrain
757  Utilities->CallLogPop(1017);
758  return; // no further action, user has to remove or work around
759  }
761  {
763  }
765  {
767  }
769  // introduced at v1.2.0, formerly 'TimeTimeLocArrived = false' was included
770  // in the next condition 'if(!Stopped() && !SPADFlag)' which led to repeated arrival messages if signaller control allowed a train
771  // to move & then stop again at the same station
772  {
773  TimeTimeLocArrived = false;
774  }
775  if(!Stopped() && !SPADFlag && !TrainFailed)
776  {
778  }
779  // set or release StoppedAtBuffers if fully on 2 elements depending on LeadElement
780  // Note that if LeadElement == Buffers train must be facing the buffer so no need to check orientation
781 /* old version where force a stop at buffers regardless of speed
782  if((Straddle == LeadMid) && (LeadElement > -1) && (Track->TrackElementAt(, LeadElement).TrackType == Buffers)) StoppedAtBuffers = true;
783  else StoppedAtBuffers = false;
784 */
785 
786  // new version where crash if run into buffers
787  if(!Crashed)
788  {
789  if((Straddle == LeadMid) && (LeadElement > -1) && (Track->TrackElementAt(602, LeadElement).TrackType == Buffers))
790  {
791  if(ExitSpeedFull > 1)
792  {
793  Crashed = true;
797  // SendMissedActionLogs(3, -1, ActionVectorEntryPtr);//-1 is a marker for send messages for all remaining entries, including Fer if present
798  // no need for missed action logs - will be sent when train removed
799  StoppedAtBuffers = false;
800  }
802  // stopped at location & stopped without power take precedence
803  {
804  StoppedAtBuffers = true;
805  }
806  else
807  {
808  StoppedAtBuffers = false;
809  }
810  }
811  else
812  {
813  StoppedAtBuffers = false;
814  }
815  }
816  else
817  {
818  StoppedAtBuffers = false;
819  }
820  // if crashed don't want stopped at buffers set
821 
822  // also crash if run into a level crossing that is changing or has barriers up
823  if(!Crashed)
824  {
825  if((Straddle == LeadMid) && (LeadElement > -1) && (ExitSpeedFull > 1))
826  {
827  int H = Track->TrackElementAt(873, LeadElement).HLoc;
828  int V = Track->TrackElementAt(874, LeadElement).VLoc;
829  if(Track->IsLCAtHV(40, H, V) && !Track->IsLCBarrierDownAtHV(2, H, V))
830  {
831  Crashed = true;
835  // no need for missed action logs - will be sent when train removed
836  }
837  }
838  }
840  {
842  }
843  // set or reset HoldAtLocationInTTMode (if true then actions are needed before train departs)
844  if((TrainMode == Timetable) && StoppedAtLocation && (ActionVectorEntryPtr->Command != "")) //if Command == "" then either TimeLoc or TimeTimeLoc
845  {
846  HoldAtLocationInTTMode = true;
847  }
848  else if(TrainMode == Timetable)
849  {
850  HoldAtLocationInTTMode = false;
851  }
852  // in Signaller mode HoldAtLocationInTTMode not changed
853 
854  // check if departure pending & set times unless already set
855  if(TrainMode == Timetable)
856  {
858  // && !StoppedAtBuffers) - drop this, set times whether or not at buffers
859  {
860  if(ActionVectorEntryPtr->DepartureTime > TDateTime(-1))
861  {
863  if(ReleaseTime <= LastActionTime + TDateTime(30.0 / 86400))
864  {
865  ReleaseTime = LastActionTime + TDateTime(30.0 / 86400);
866  }
867  TRSTime = ReleaseTime - TDateTime(10.0 / 86400);
868  DepartureTimeSet = true;
869  }
870  }
871  }
873  {
874  OpTimeToAct = CalcTimeToAct(0); // called after ReleaseTime set
875  // calculate every 1 sec (in real time, not timetable time) for all trains
876  }
877  // check if being held at location pending any actions & deal with them if time appropriate & >= 30s since LastActionTime
878  if(TrainMode == Timetable)
879  {
880  if((ActionVectorEntryPtr->Command != "Frh") && (ActionVectorEntryPtr->Command != "Frh-sh"))
881  {
882  RemainHereLogNotSent = true;
883  }
885  {
886  // ignore TimeLoc & TTLoc departures
887  // Action logs given in functions
889  LastActionTime + TDateTime(30.0 / 86400)))
890  {
891  if(ActionVectorEntryPtr->Command == "fsp")
892  {
893  // added for v1.3.2 because when add new train to TrainVector 'this' address likely invalidated, hence make no more changes to 'this' train. Next clock cycle will deal with any required changes
894  FrontTrainSplit(0);
895  if(TrainFailurePending) // ok, stopped so PlotElements set
896  {
897  TrainHasFailed(0);
898  }
899  Utilities->CallLogPop(2041);
900  return;
901  }
902  else if(ActionVectorEntryPtr->Command == "rsp")
903  {
904  // added for v1.3.2 because when add new train to TrainVector 'this' address likely invalidated, hence make no more changes to 'this' train. Next clock cycle will deal with any required changes
905  RearTrainSplit(0);
906  if(TrainFailurePending) // ok, stopped so PlotElements set
907  {
908  TrainHasFailed(1);
909  }
910  Utilities->CallLogPop(2042);
911  return;
912  }
913  else if(ActionVectorEntryPtr->Command == "Fjo")
914  {
915  FinishJoin(0);
916  }
917  else if(ActionVectorEntryPtr->Command == "jbo")
918  {
919  JoinedBy(0);
920  }
921  else if(ActionVectorEntryPtr->Command == "cdt")
922  {
924  }
925  else if(ActionVectorEntryPtr->Command == "Fns")
926  {
927  NewTrainService(0);
928  }
929  else if(ActionVectorEntryPtr->Command == "Frh")
930  {
931  RemainHere(0);
932  }
933  else if(ActionVectorEntryPtr->Command == "Fer")
934  {
935  TimetableFinished = true;
936  }
937  // other aspects of 'Fer' dealt with in TTrain::Update()
938  else if(ActionVectorEntryPtr->Command == "F-nshs")
939  {
941  }
942  else if(ActionVectorEntryPtr->Command == "Frh-sh")
943  {
945  }
946  else if(ActionVectorEntryPtr->Command == "Fns-sh")
947  {
949  }
950 /*
951  F-nshs (FNSShuttle) = Finish New Service (Shuttle) = finish, form new shuttle service in same direction, details =
952  shuttle headcode (no train creation)
953  Frh-sh (TimeCmdHeadCode) = Finish then restart as a shuttle using Snt-sh or Sns-sh, when all shuttle repeats done
954  remain here
955  Fns-sh (FSHNewService) = Finish then restart as a shuttle using Snt-sh or Sns-sh, when all shuttle repeats done
956  form new service via Sns-fsh using the NonRepeatingShuttleLinkHeadCode
957 */
958  }
959  }
960  else
961  {
963  {
965  }
966  }
967  }
968  if(TrainMode == Timetable)
969  {
970  if(StoppedAtBuffers)
971  {
972  // error if buffers (& element before it) not at a location, or if buffer location different to ActionVectorEntryPtr location
973  // if buffer location same as ActionVectorEntryPtr location & not Frh then error will be given for inability to depart
974  AnsiString BufferLocation = Track->TrackElementAt(604, LeadElement).ActiveTrackElementName;
975  if(BufferLocation == "")
976  {
978  }
979  AnsiString ExpectedLocation = ActionVectorEntryPtr->LocationName;
980  if((BufferLocation == "") || (BufferLocation != ExpectedLocation))
981  {
985  {
987  // SendMissedActionLogs(-1, ActionVectorEntryPtr);//-1 is a marker for send messages for all remaining entries, including Fer if present
988  // Drop missed actions so user can still use sig mode to get back on track
990  }
991  if(TrainFailurePending) // ok, stopped so PlotElements set
992  {
994  TrainHasFailed(2);
995  }
996  Utilities->CallLogPop(1020);
997  return;
998  }
999  else if((BufferLocation != "") && (BufferLocation == ExpectedLocation) && DepartureTimeSet && !StoppedAtLocation && (TrainController->TTClockTime >
1000  ReleaseTime))
1001  {
1004  {
1007  // SendMissedActionLogs(-1, ActionVectorEntryPtr);//-1 is a marker for send messages for all remaining entries, including Fer if present
1008  // Drop missed actions so user can still use sig mode to get back on track
1010  }
1011  if(TrainFailurePending) // ok, stopped so PlotElements set
1012  {
1014  TrainHasFailed(3);
1015  }
1016  Utilities->CallLogPop(1397);
1017  return;
1018  }
1019  }
1020  else
1021  {
1023  }
1024  }
1025  else
1026  {
1028  }
1029  if(TrainMode == Timetable)
1030  {
1032  {
1034  }
1036  {
1038  }
1039  }
1040  // Pick up element next to the train front (if exists) to check for calling-on, restart after a cleared signal, or
1041  // restart after stopped for train in front
1042  int NextElementPosition, NextEntryPos;
1043 
1044  if(LeadElement > -1) // if an exit continuation then not set
1045  {
1046  if((Track->TrackElementAt(186, LeadElement).TrackType != Points) || ((LeadEntryPos != 0) && (LeadEntryPos != 2)))
1047  {
1049  }
1050  else if((Track->TrackElementAt(187, LeadElement).TrackType == Points) && ((LeadEntryPos == 0) || (LeadEntryPos == 2)))
1051  {
1052  if(Track->TrackElementAt(188, LeadElement).Attribute == 0)
1053  {
1054  LeadExitPos = 1;
1055  }
1056  else
1057  {
1058  LeadExitPos = 3;
1059  }
1060  }
1061  NextElementPosition = Track->TrackElementAt(189, LeadElement).Conn[LeadExitPos];
1062  NextEntryPos = Track->TrackElementAt(190, LeadElement).ConnLinkPos[LeadExitPos];
1063  }
1064  else
1065  {
1066  NextElementPosition = -1;
1067  NextEntryPos = -1;
1068  }
1069  if((NextElementPosition > -1) && (NextEntryPos > -1))
1070  // may be buffers or continuation so need this check
1071  {
1072 /*
1073  Check whether calling-on conditions met:-
1074  a) approaching train has stopped at a signal but not at a location;
1075  b) if there is a facing train at the station, it is being held appropriately (must be awaiting a join (Fjo or jbo) or a
1076  change of direction (cdt), remaining here (Frh), or under signaller control);
1077  c) at least 1 platform available for the approaching train;
1078  d) points (if any) set for direct route into platform;
1079  e) approaching train is to stop at station;
1080  f) no more facing signals between train and platform;
1081  g) [dropped g]
1082  h) train in front preventing route being set far enough to release stop signal;
1083  i) train in front not exiting at continuation;
1084  j) signal must be within 4km of the stop platform;
1085  k) [dropped (k), now can set a reoute or part route into platform so can set points more easily];
1086  l) no existing route conflicts with the route into the platform; and
1087  m) not failed or without power (these added at v2.10.0)
1088  If all OK & route or part route not already set then set an unrestricted route into the station (just to the first platform), to prevent point changing or
1089  other route conflicts - if a partial route set than can still change points outside the route or have a route conflict if another route is set.
1090 */
1091  if(TrainMode == Timetable)
1092  {
1093  if(CallingOnAllowed(0)) //returns false if failed or no power (modified afer v2.9.2)
1094  {
1095  CallingOnFlag = true;
1096  PlotTrainWithNewBackgroundColour(1, clCallOnBackground, Display); // calling-on background
1097  }
1098  else
1099  {
1100  if(CallingOnFlag) //TrainHasFailed sets this flag to false. Failed & no power conditions added at v2.10.0 as a result of Albie Vowles error
1101  //notified by email on 02/08/21
1102  {
1103  if(!TrainFailed) //shouldn't be needed but include for safety at v2.10.0
1104  {
1106  }
1107  }
1108  CallingOnFlag = false;
1109  }
1110  }
1111  if(StoppedAtSignal && ((Track->TrackElementAt(191, NextElementPosition).Attribute > 0) || AllowedToPassRedSignal) && !TrainFailed && !StoppedAtLocation)
1112  {
1113  //'&& !StoppedAtLocation' added at v2.7.0 as if had been stopped at signal before tt control restored then background colour changed to normal when signal changed from red
1114  // reset PassRedSignal when reached half-way point in next element, if reset here then SetTrainMovementValues
1115  // sets StoppedAtSignal again & train doesn't move
1116  StoppedAtSignal = false;
1117  // need to recalculate exit times since old entry time expired. Straddle now at MidLag with front of train on MidElement
1118  // hence use MidElement for the calculation so same as would have been used if signal not red, when Straddle was
1119  // LeadMidLag and front of train was on LeadElement (after the current move)
1121  EntrySpeed = 0;
1123  FirstHalfMove = true;
1124  SetTrainMovementValues(4, NextElementPosition, NextEntryPos);
1125  // NextElement is the element to be entered
1126  }
1128  {
1129  if(ClearToNextSignal(0))
1130  {
1131  StoppedForTrainInFront = false;
1132  BeingCalledOn = false;
1133  EntrySpeed = 0;
1135  FirstHalfMove = true;
1136  SetTrainMovementValues(16, NextElementPosition, NextEntryPos);
1137  }
1138  else
1139  {
1140  if(TrainFailurePending) // ok, stopped so PlotElements set
1141  {
1142  TrainHasFailed(4);
1143  }
1144  Utilities->CallLogPop(1097);
1145  return;
1146  }
1147  }
1148  }
1149  if((Straddle == MidLag) && (LeadElement != -1))
1150  // later check only for Straddle == LeadMid, so need this check here for initial train start
1151  {
1153  }
1154 /* Logic below as follows: This check is made to allow a restart if had StoppedAtLocation or StoppedForTrainInFront or
1155  both but potentially able to restart (i.e. not at buffers, not crashed, not derailed, not held at location, departure
1156  time due, no train in front now & no other stop condition). Note that can be StoppedForTrainInFront when not at a
1157  location since this is set in SetTrainMovementValues whenever a train has zero EntrySpeed and there is a train in front,
1158  which could be when start as Snt.
1159  If StoppedForTrainInFront but not StoppedAtLocation then need to set TRSTime high so pink not plotted, and ReleaseTime
1160  low so can restart if appropriate. BeingCalledOn was set so that when train stopped at a station it wouldn't restart
1161  until the line was clear of trains up to the next signal. Hence check whether BeingCalledOn & if so set
1162  StoppedForTrainInFront, this ensures two things - that the restart check is carried out at each cycle and also that
1163  a restart won't happen until the line is clear to the next signal, regardless of whether or not the ReleaseTime has been
1164  reached.
1165  Then check if TRS time reached & change background to pink if so, & check if release time reached & if so change
1166  background to white and clear StoppedAtLocation. Then make check of station name, and recheck StoppedForTrainInFront,
1167  if it's set check if ClearToNextSignal and if so clear StoppedForTrainInFront & BeingCalledOn. If not ClearToNextSignal
1168  then return. If either not StoppedForTrainInFront or ClearToNextSignal then restart, calling SetTrainMovementValues &
1169  sending a message to the performancelog.
1170 */
1171 
1172  if(TrainMode == Timetable)
1173  {
1175  {
1176  if(BeingCalledOn)
1177  {
1178  StoppedForTrainInFront = true;
1179  }
1181  {
1183  }
1185  {
1186  // value updated at every scheduled departure & arrival
1188  AnsiString StationName;
1190  {
1192  }
1194  {
1196  }
1197  else
1198  {
1199  throw Exception("Error - Stopped at through station but neither lead nor mid elements have a name");
1200  }
1201  EntrySpeed = 0;
1205  FirstHalfMove = true;
1206  StoppedAtLocation = false;
1207 
1208  if((PowerAtRail < 1) && EntrySpeed < 1) // added at v2.4.0
1209  {
1210  StoppedWithoutPower = true;
1211  }
1212  if((NextElementPosition > -1) && (NextEntryPos > -1))
1213  // condition check added for SloughIECC error reported by James U
1214  {
1215  if((Track->TrackElementAt(720, NextElementPosition).Config[Track->GetNonPointsOppositeLinkPos(NextEntryPos)] == Signal) &&
1216  (Track->TrackElementAt(721, NextElementPosition).Attribute == 0))
1217  {
1218  StoppedAtSignal = true;
1219  if(!StoppedWithoutPower)
1220  // if stopped without power just keep existing background colour
1221  {
1223  // TrainController->LogActionError(40, HeadCode, "", SignalHold, Track->TrackElementAt(755, NextElementPosition).ElementID);
1224  }
1225  }
1226  }
1228  {
1229  TimeTimeLocArrived = false;
1230  LogAction(27, HeadCode, "", Depart, StationName, ActionVectorEntryPtr->DepartureTime, false);
1231  // no warning for TimeTimeLoc departure
1232  }
1233  else
1234  {
1236  }
1237  DepartureTimeSet = false;
1238  // no need to set LastActionTime for a departure
1240  // advance pointer beyond departure action - (this line (& LogAction) used to be at the end - see
1241  // note
1242 /*
1243  Note: If train stops at station after call on with a TimeTimeLoc loaded, and before the normal stop point, then when
1244  SetTrainMovementValues called it assumes a stop at the stop point because the ActionVectorEntryPtr points to a name
1245  when NameInTimetableBeforeCDT is called and the stop positions are valid. So next element train movement is based on
1246  this calculation. However, when the departure time check is made (it is during this function when SetTrainMovementValues
1247  is called), the ActionVectorEntryPtr is advanced at the end past the departure location, so at the next element when
1248  SetTrainMovementValues is called again, all is normal, i.e. the train doesn't stop again at the location. But to cure
1249  the problem move the ActionVectorEntryPtr increment to before SetTrainMovementValues.
1250 */
1252  {
1253  StoppedAtBuffers = true;
1254  }
1255  else if(!StoppedWithoutPower)
1256  // if buffers or no power, don't set values
1257  {
1259  {
1260  SetTrainMovementValues(12, NextElementPosition, NextEntryPos);
1261  // NextElement is the element to be entered
1262  }
1263  else
1264  {
1266  // use LeadElement for an exit continuation
1267  }
1268  }
1269  }
1270  }
1271  }
1272  if(Straddle == LeadMidLag) //train on a half element
1273  {
1275  {
1276  Utilities->CallLogPop(654);
1277  return;
1278  }
1279  }
1280  else //train fully on 2 elements
1281  {
1283  {
1284  Utilities->CallLogPop(655);
1285  return;
1286  }
1287  }
1288  if((LeadElement > -1) && (MidElement > -1))
1289  {
1291  {
1292  // don't allow to stop if exiting at a continuation as causes problems if try to change direction
1293  // if entering at continuation & LeadElement is a continuation then MidElement will be -1
1294  //don't need to check for MidElement being continuation because popup menu won't show when exiting at continuation so SignallerStoppingFlag can't be set
1295  SignallerStoppingFlag = false;
1296  StepForwardFlag = false;
1297  }
1298  }
1299  if(Stopped())
1300  // this is what prevents another movement if the train is stopped
1301  {
1302  if(TrainFailurePending) // ok, stopped so PlotElements set
1303  {
1304  TrainHasFailed(5);
1305  }
1306  BrakeRate = 0;
1307  Utilities->CallLogPop(656);
1308  return;
1309  }
1310  // here when ready for next move
1311 
1312  //added at v2.10.0 to set SPADFlag if red signal immediately ahead (as it will be if in a locked route)
1313  //check if due to run past a red signal & if so set SPADFlag (SetTrainMovementValues & its SPAD check only called when arrive fully on 2 elements)
1314  if(Straddle == LeadMid) //fully on 2 elements
1315  {
1316  if(LeadElement > -1)
1317  {
1318  if(Track->TrackElementAt(1402, LeadElement).Conn[LeadExitPos] > -1)
1319  {
1321  if(TIF.TrackType == SignalPost)
1322  {
1323  int TIFEntryPos = Track->TrackElementAt(1405, LeadElement).ConnLinkPos[LeadExitPos];
1324  int TIFExitPos = 0;
1325  if(TIFEntryPos == 0)
1326  {
1327  TIFExitPos = 1;
1328  }
1329  if((TIF.Config[TIFExitPos] == Signal) && TIF.Attribute == 0 && (ExitSpeedHalf > 1) && !AllowedToPassRedSignal) //use ExitSpeedHalf as may have been stopped at signal so entryspeed is 0
1330  {
1331  SPADFlag = true; // user has to intervene to reset & restart after spad
1332  }
1333  }
1334  }
1335  }
1336  }
1337 
1338  // check for train in front & if so stop at next access (when train fully on element next to train)
1339  if((TrainMode == Signaller) && (Straddle == LeadMidLag))
1340  // SetTrainMovementValues brakes & stops signaller mode train for a train in front using local
1341  // variable TrainInFrontInSignallerModeFlag
1342  {
1343  if(LeadElement > -1)
1344  {
1345  int NextPos = Track->TrackElementAt(649, LeadElement).Conn[LeadExitPos];
1346  int NextEntryPos = Track->TrackElementAt(650, LeadElement).ConnLinkPos[LeadExitPos];
1347  if(Track->OtherTrainOnTrack(1, NextPos, NextEntryPos, TrainID))
1348  // true if another train on NextEntryPos track whether bridge or not
1349  {
1350  StoppedForTrainInFront = true;
1351  }
1352  else
1353  {
1354  StoppedForTrainInFront = false;
1355  }
1356  }
1357  }
1358  if((Straddle == LeadMid) && SPADFlag)
1359  // give message + plot background when ready to move half past the signal
1360  {
1361  if(NextElementPosition > -1)
1362  {
1363  if((Track->TrackElementAt(662, NextElementPosition).Config[Track->GetNonPointsOppositeLinkPos(NextEntryPos)] == Signal) &&
1364  (Track->TrackElementAt(663, NextElementPosition).Attribute == 0))
1365  {
1366  AnsiString LocID = AnsiString(Track->TrackElementAt(664, NextElementPosition).ElementID);
1368  // if goes past 2 signals then give message twice
1370  }
1371  }
1372  }
1373  if(Straddle == LeadMidLag)
1374  // During this function train moves fully onto 2 elements, Lead & Mid, so set next 2 moves from here for the element after Lead
1375  {
1376  // if SPADFlag set allow to keep moving until signal obscured before setting background colour, & stop only when ExitSpeedFull is 0
1377  if(SPADFlag)
1378  {
1379  if(ExitSpeedFull == 0)
1380  {
1381  StoppedAfterSPAD = true;
1382  // but don't want to stop until have moved fully onto element, hence stop test is before this check
1383  }
1384  }
1386  {
1387  if(ExitSpeedFull == 0)
1388  {
1389  // only reach here when will stop on LeadMid, because SetTrainMovementValues called after this (i.e. ExitSpeedFull becomes 0 if not 0 now
1390  // after this test), and Straddle == LeadMidLag so not accessed at the half-move point, hence only reached at the full move
1391  // point when the speed is 0. So, colour change won't occur until fully stopped (early in UpdateTrain()), and the log message
1392  // is sent at the right time and once only.
1393  SignallerStopped = true;
1394  // but don't want to stop until have moved fully onto element, hence stop test is before this check
1395  StepForwardFlag = false;
1396  SignallerStoppingFlag = false;
1397  TTrackElement TE;
1398  AnsiString Loc = "";
1399  bool LocNamed = false;
1400  if(LeadElement > -1)
1401  {
1402  TE = Track->TrackElementAt(782, LeadElement);
1403  if(TE.ActiveTrackElementName != "")
1404  {
1405  Loc = TE.ActiveTrackElementName;
1406  LocNamed = true;
1407  }
1408  else
1409  {
1410  Loc = "track element " + TE.ElementID;
1411  }
1412  }
1413  if((MidElement > -1) && !LocNamed)
1414  {
1415  TE = Track->TrackElementAt(783, MidElement);
1416  if(TE.ActiveTrackElementName != "")
1417  {
1418  Loc = TE.ActiveTrackElementName;
1419  LocNamed = true;
1420  }
1421  else if(Loc == "")
1422  {
1423  Loc = "track element " + TE.ElementID;
1424  }
1425  }
1426  if(Loc == "")
1427  {
1428  Loc = "outside railway";
1429  // must have stopped after left at a continuation (because both lead & mid == -1)
1430  }
1431  else
1432  {
1433  Loc = "at " + Loc;
1434  }
1435  LogAction(30, HeadCode, "", SignallerStop, Loc, TrainController->TTClockTime, false); // false for warning
1436  }
1437  }
1438  if(LeadElement > -1) // if an exit continuation then not set
1439  {
1440  if((Track->TrackElementAt(202, LeadElement).TrackType != Points) || ((LeadEntryPos != 0) && (LeadEntryPos != 2)))
1441  {
1443  }
1444  else if((Track->TrackElementAt(203, LeadElement).TrackType == Points) && ((LeadEntryPos == 0) || (LeadEntryPos == 2)))
1445  {
1446  if(Track->TrackElementAt(204, LeadElement).Attribute == 0)
1447  {
1448  LeadExitPos = 1;
1449  }
1450  else
1451  {
1452  LeadExitPos = 3;
1453  }
1454  }
1455  NextElementPosition = Track->TrackElementAt(205, LeadElement).Conn[LeadExitPos];
1456  NextEntryPos = Track->TrackElementAt(206, LeadElement).ConnLinkPos[LeadExitPos];
1457  }
1458  else
1459  {
1460  NextElementPosition = -1;
1461  NextEntryPos = -1;
1462  }
1465  FirstHalfMove = true; //will be when finished the move onto 2 elements during this function
1466 
1467  if((PowerAtRail < 1) && EntrySpeed < 1) // added at v2.4.0
1468  {
1469  StoppedWithoutPower = true;
1470  }
1471  if((NextElementPosition > -1) && (NextEntryPos > -1) && !SPADFlag)
1472  // may be buffers or continuation. SPADFlag added at v2.1.0
1473  // so don't override the SPAD colour & don't set StoppedAtSignal
1474  {
1475  if((Track->TrackElementAt(207, NextElementPosition).Config[Track->GetNonPointsOppositeLinkPos(NextEntryPos)] == Signal) &&
1476  (Track->TrackElementAt(208, NextElementPosition).Attribute == 0) && (ExitSpeedFull < 1) && !StoppedAtLocation)
1477  {
1478  StoppedAtSignal = true;
1479  if(!StoppedWithoutPower)
1480  // leave background as is if no power, but set StoppedAtSignal
1481  {
1483  }
1484  // TrainController->LogActionError(41, HeadCode, "", SignalHold, Track->TrackElementAt(756, NextElementPosition).ElementID);
1485  }
1486  }
1487  if(!Stopped())
1488  {
1489  if((NextElementPosition > -1) && (NextEntryPos > -1))
1490  // may be buffers or continuation (skip SetTrainMovementValues if buffers, if
1491  // a stop element that isn't buffers - e.g. station, then will skip the calcs
1492  // during SetTrainMovementValues to avoid trying to divide by zero - see that
1493  // function for fuller explanation
1494  {
1495  SetTrainMovementValues(8, NextElementPosition, NextEntryPos);
1496  // NextElement is the element to be entered
1497  }
1498  // follow the continuation exits:-
1499  else if((LeadElement > -1) && (Track->TrackElementAt(209, LeadElement).TrackType == Continuation))
1500  {
1502  // Use LeadElement for calcs if lead is a continuation
1503  }
1504  else if((MidElement > -1) && (Track->TrackElementAt(210, MidElement).TrackType == Continuation))
1505  {
1507  // Use MidElement for calcs if mid is a continuation
1508  }
1509  else if((LagElement > -1) && (Track->TrackElementAt(211, LagElement).TrackType == Continuation))
1510  {
1512  // Use LagElement for calcs if lag is a continuation
1513  }
1514  }
1515  // remove route elements if not autosigs - this section moved from below, was under LagElement > -1 condition but needs to cover LagElement == -1
1516  if(AllRoutes->GetRouteTypeAndGraphics(2, LeadElement, LeadEntryPos, EXGraphicPtr, EntryDirectionGraphicPtr) == TAllRoutes::NotAutoSigsRoute)
1517  // Trains may not be in a route
1518  // Since Straddle = LeadMidLag at this point the train is going to move fully off the existing Lag & fully onto existing Lead element during this function
1519  {
1520  // NB if LeadElement == -1 then the above test returns false
1521  int TempH = Track->TrackElementAt(213, LeadElement).HLoc;
1522  int TempV = Track->TrackElementAt(214, LeadElement).VLoc;
1523  int TempELink = Track->TrackElementAt(215, LeadElement).Link[LeadEntryPos];
1524  TAllRoutes::TRouteElementPair FirstPair, SecondPair;
1525  FirstPair = AllRoutes->GetRouteElementDataFromRoute2MultiMap(10, TempH, TempV, SecondPair);
1526  if((FirstPair.first > -1) && (AllRoutes->GetFixedRouteAt(143, FirstPair.first).GetFixedPrefDirElementAt(153,
1527  FirstPair.second).GetELink() == TempELink))
1528  {
1529  AllRoutes->RemoveRouteElement(10, TempH, TempV, TempELink);
1530  }
1531  else if((SecondPair.first > -1) && (AllRoutes->GetFixedRouteAt(144, SecondPair.first).GetFixedPrefDirElementAt(154,
1532  SecondPair.second).GetELink() == TempELink))
1533  {
1534  AllRoutes->RemoveRouteElement(11, TempH, TempV, TempELink);
1535  }
1536  }
1537  if(AllRoutes->GetRouteTypeAndGraphics(3, MidElement, MidEntryPos, EXGraphicPtr, EntryDirectionGraphicPtr) == TAllRoutes::NotAutoSigsRoute)
1538  // Trains may not be in a route
1539  {
1540  int TempH = Track->TrackElementAt(216, MidElement).HLoc;
1541  int TempV = Track->TrackElementAt(217, MidElement).VLoc;
1542  int TempELink = Track->TrackElementAt(218, MidElement).Link[MidEntryPos];
1543  TAllRoutes::TRouteElementPair FirstPair, SecondPair;
1544  FirstPair = AllRoutes->GetRouteElementDataFromRoute2MultiMap(11, TempH, TempV, SecondPair);
1545  if((FirstPair.first > -1) && (AllRoutes->GetFixedRouteAt(145, FirstPair.first).GetFixedPrefDirElementAt(155,
1546  FirstPair.second).GetELink() == TempELink))
1547  {
1548  AllRoutes->RemoveRouteElement(12, TempH, TempV, TempELink);
1549  }
1550  else if((SecondPair.first > -1) && (AllRoutes->GetFixedRouteAt(146, SecondPair.first).GetFixedPrefDirElementAt(156,
1551  SecondPair.second).GetELink() == TempELink))
1552  {
1553  AllRoutes->RemoveRouteElement(13, TempH, TempV, TempELink);
1554  }
1555  }
1556  if(AllRoutes->GetRouteTypeAndGraphics(4, LagElement, LagEntryPos, EXGraphicPtr, EntryDirectionGraphicPtr) == TAllRoutes::NotAutoSigsRoute)
1557  // Trains may not be in a route
1558  {
1559  int TempH = Track->TrackElementAt(219, LagElement).HLoc;
1560  int TempV = Track->TrackElementAt(220, LagElement).VLoc;
1561  int TempELink = Track->TrackElementAt(221, LagElement).Link[LagEntryPos];
1562  TAllRoutes::TRouteElementPair FirstPair, SecondPair;
1563  FirstPair = AllRoutes->GetRouteElementDataFromRoute2MultiMap(12, TempH, TempV, SecondPair);
1564  if((FirstPair.first > -1) && (AllRoutes->GetFixedRouteAt(147, FirstPair.first).GetFixedPrefDirElementAt(157,
1565  FirstPair.second).GetELink() == TempELink))
1566  {
1567  AllRoutes->RemoveRouteElement(14, TempH, TempV, TempELink);
1568  }
1569  else if((SecondPair.first > -1) && (AllRoutes->GetFixedRouteAt(148, SecondPair.first).GetFixedPrefDirElementAt(158,
1570  SecondPair.second).GetELink() == TempELink))
1571  {
1572  AllRoutes->RemoveRouteElement(15, TempH, TempV, TempELink);
1573  }
1574  AllRoutes->CheckMapAndRoutes(8); // test
1575  }
1576  if(LagElement > -1)
1577  // not entering at a continuation so can deal with train leaving the lag element
1578  {
1580  // amended below so route elements removed for the complete train (for NotAutoSigsRoutes), so train never standing on a route once it
1581  // starts moving, covers for eliminating route when train reaches buffers, and prevents odd route segments when route extended while
1582  // straddling 3 elements (formerly the last segment was replotted as a route & stayed plotted
1583 
1584  TPrefDirElement PrefDirElement;
1585  // plot locked route marker for any element if appropriate (i.e. if a locked AutoSigs route) but only when train leaves element completely
1586  // as this is a 16x16 graphic
1588  {
1590  RailGraphics->LockedRouteCancelPtr[PrefDirElement.GetELink()]);
1591  }
1592  if(ContinuationExit(2, LagElement, LagExitPos)) // true if Element is a continuation and Exitpos is the continuation end
1593  {
1594  int RouteNumber;
1595  TrainGone = true;
1596  // flag to indicate train to be deleted - outside this function
1598  {
1599  TTrainController::TContinuationAutoSigEntry ContinuationAutoSigEntry;
1600  ContinuationAutoSigEntry.RouteNumber = RouteNumber;
1601  // calc distance from & inc last signal to exit
1602  int LastElement = LagElement, LastExitPos = LagExitPos, CumDistance = 0;
1603  int NewLastElement = 0, NewLastExitPos = 0;
1604  // need above because can't change LastElement & LastExitPos until both new values obtained
1605  // while((Track->TrackElementAt(684, LastElement).Config[LastExitPos] != Signal) && (CumDistance < 1200)) as was
1606  while((Track->TrackElementAt(913, LastElement).Config[LastExitPos] != Signal) && (CumDistance < 1200) && (Track->TrackElementAt(897,
1607  LastElement).TrackType != Points))
1608  // extra condition above added because of Moric1998's error (see email of 24/03/2016), where had an autosigs route across points, and another continuation on track not occupied by route so
1609  // failed when found a new element = -1 when tried to cross the continuation. Note this routine can only deal with non points as it uses GetNonPointsOppositeLinkPos
1610  // leave CumDistance as it was in these circumstances.
1611  {
1612  if(LastExitPos < 2)
1613  {
1614  CumDistance += Track->TrackElementAt(685, LastElement).Length01;
1615  }
1616  else
1617  {
1618  CumDistance += Track->TrackElementAt(686, LastElement).Length23;
1619  }
1620  NewLastElement = Track->TrackElementAt(687, LastElement).Conn[Track->GetNonPointsOppositeLinkPos(LastExitPos)];
1621  if(NewLastElement == -1)
1622  // this will catch buffers or any other connection failure
1623  {
1624  throw Exception("Error, Connection = -1 in Continuation loop in UpdateTrain");
1625  }
1626  NewLastExitPos = Track->TrackElementAt(688, LastElement).ConnLinkPos[Track->GetNonPointsOppositeLinkPos(LastExitPos)];
1627  if(NewLastExitPos == -1)
1628  {
1629  throw Exception("Error, ConnLinkPos = -1 in Continuation loop in UpdateTrain");
1630  }
1631  LastElement = NewLastElement;
1632  LastExitPos = NewLastExitPos;
1633  }
1634  // if at signal add this in too
1635  if(CumDistance < 1200)
1636  {
1637  CumDistance += Track->TrackElementAt(689, LastElement).Length01; // only need 01 for signal
1638  }
1639  // now have distance including the signal, if >=1200m use 100m (for a signal immediately after the continuation)
1640  // else use 1200m - CumDistance
1641  int FirstDistance = 0;
1642  if(CumDistance >= 1200)
1643  {
1644  FirstDistance = 100;
1645  }
1646  else
1647  {
1648  FirstDistance = 1200 - CumDistance;
1649  }
1650  if(FirstDistance < 100)
1651  {
1652  FirstDistance = 100; // don't allow < 100
1653  }
1654  // can now calc the time delays in seconds - FirstDelay, SecondDelay & ThirdDelay, these are doubles
1655  // BUT - first check whether ExitSpeedFull is very low (Mark had divide by zero error with zero exit speed using v2.4.0)
1656  if(ExitSpeedFull > 20.0)
1657  {
1658  ContinuationAutoSigEntry.FirstDelay = 3.6 * double(FirstDistance) / ExitSpeedFull;
1659  // speed in km/h & distance in m so mult by 3.6 to bring to secs
1660  ContinuationAutoSigEntry.SecondDelay = ContinuationAutoSigEntry.FirstDelay + 4320.0 / ExitSpeedFull;
1661  // 4320.0 = 3.6 * 1200, .0 to make it a double
1662  ContinuationAutoSigEntry.ThirdDelay = ContinuationAutoSigEntry.SecondDelay + 4320.0 / ExitSpeedFull;
1663  }
1664  else
1665  {
1666  ContinuationAutoSigEntry.FirstDelay = 60.0; // 60 secs
1667  ContinuationAutoSigEntry.SecondDelay = 120.0;
1668  ContinuationAutoSigEntry.ThirdDelay = 180.0;
1669  }
1670  ContinuationAutoSigEntry.AccessNumber = 0;
1671  ContinuationAutoSigEntry.PassoutTime = TrainController->TTClockTime;
1673  {
1675  for(VectorIT = TrainController->ContinuationAutoSigVector.begin(); VectorIT != TrainController->ContinuationAutoSigVector.end();
1676  VectorIT++)
1677  {
1678  if(VectorIT->RouteNumber == RouteNumber)
1679  {
1680  // another train has passed out of same route so erase earlier entry
1681  TrainController->ContinuationAutoSigVector.erase(VectorIT);
1682  break;
1683  }
1684  }
1685  }
1686  TrainController->ContinuationAutoSigVector.push_back(ContinuationAutoSigEntry);
1687  }
1689  // need to plot this as returning early so will miss the later plot (not a bridge so don't need PlotAlternativeTrackRouteGraphic)
1690  Display->Update();
1691  // need to keep this since Update() not called for PlotSmallOutput as too slow
1692  Utilities->CallLogPop(659);
1693  return;
1694  }
1695  // above covers for exiting at continuation, need XLinkPos check to exclude entering at a continuation
1696  if(LeadElement > -1)
1697  {
1699  // changed to lead so reset early
1700  {
1701  Track->TrackElementAt(225, LeadElement).Attribute = 0; // red
1703  // don't plot if zoomed out
1704  if(!Display->ZoomOutFlag)
1705  {
1707  }
1708  // covers signal resetting in same direction
1709  }
1710  }
1712  {
1713  if(AllRoutes->GetRouteTypeAndGraphics(5, LagElement, LagEntryPos, EXGraphicPtr, EntryDirectionGraphicPtr) == TAllRoutes::AutoSigsRoute)
1714  {
1715  Display->PlotOutput(23, Track->TrackElementAt(227, LagElement).HLoc * 16, Track->TrackElementAt(228, LagElement).VLoc * 16, EXGraphicPtr);
1716  Display->PlotOutput(24, Track->TrackElementAt(229, LagElement).HLoc * 16, Track->TrackElementAt(230, LagElement).VLoc * 16, EntryDirectionGraphicPtr);
1717  TPrefDirElement PrefDirElement;
1718  // plot locked route marker for same side signal if appropriate (may be covered above but leave in), but only when train leaves element completely as this is a 16x16 graphic
1720  {
1722  RailGraphics->LockedRouteCancelPtr[PrefDirElement.GetELink()]);
1723  }
1725  LockedVectorNumber)))
1726  {
1728  }
1729  }
1730  }
1731  else if((LeadElement > -1) && (Track->TrackElementAt(233, LeadElement).TrackType == SignalPost))
1732  {
1733  Track->TrackElementAt(234, LeadElement).Attribute = 0; // red
1735  // don't plot if zoomed out
1736  if(!Display->ZoomOutFlag)
1737  {
1739  }
1740  // covers signal passed in opposite direction - replot as red, regardless of what it was before, though should already have been red
1741  }
1743  {
1744  if(AllRoutes->GetRouteTypeAndGraphics(6, LagElement, LagEntryPos, EXGraphicPtr, EntryDirectionGraphicPtr) == TAllRoutes::AutoSigsRoute)
1745  {
1746  Display->PlotOutput(26, Track->TrackElementAt(236, LagElement).HLoc * 16, Track->TrackElementAt(237, LagElement).VLoc * 16, EXGraphicPtr);
1747  Display->PlotOutput(27, Track->TrackElementAt(238, LagElement).HLoc * 16, Track->TrackElementAt(239, LagElement).VLoc * 16, EntryDirectionGraphicPtr);
1748  // below added at v1.3.0 to reset signals if back out of an autosigs route under signaller control after changing direction, when new LeadElement not on route (if it had
1749  // been the route would have been ForceCancelled). Note that the signal is not facing the direction of travel else would have entered
1750  // "if(Track->TrackElementAt(, LagElement).Config[LagExitPos] == Signal)" above and wouldn't be here
1751  int RouteNumber;
1753  // already know it's an autosigsroute, this is just to get the RouteNumber
1754  // addition below at v1.3.2 - found that a signal that had reached double yellow in ContinuationAutoSigs was reset to red when a following train's lag element
1755  // moved off a signal in the normal course of events. It was caused when a train backed out of an autosigs route under signaller control after changing
1756  // direction (see DevHistory.txt). Hence check that the train is in signaller mode and that the train's lead element isn't on the same route before calling SetRouteSignals.
1757  int RouteNumber2;
1759  // already know it's an autosigsroute, this is just to get the RouteNumber
1760  if((TrainMode == Signaller) && (RouteNumber2 != RouteNumber))
1761  // note that if not in a route (as likely) then RouteNumber2 set to -1 )
1762  {
1763  AllRoutes->GetFixedRouteAt(217, RouteNumber).SetRouteSignals(10);
1764  // this was in the 1.3.0 addition but without the condition
1765  }
1766  // end of 1.3.2 addition
1767  // end of 1.3.0.addition
1768  }
1769  TPrefDirElement PrefDirElement;
1770  // plot locked route marker for opp side signal if appropriate (may be covered above but leave in), but only when train leaves element completely as this is a 16x16 graphic (OK - Straddle == LeadMidLag)
1772  {
1774  RailGraphics->LockedRouteCancelPtr[PrefDirElement.GetELink()]);
1775  }
1776  }
1777  }
1778  }
1779  // straddle ONLY changed here, check if 'LeadMid' first & if so ready for updating Elements
1780  if(Straddle == LeadMid)
1781  {
1782  AllowedToPassRedSignal = false;
1783  // if had been allowed to pass then at this point it will move half onto signal so can be reset
1784  // if(LagElement > -1) ResetTrainElementID(LagElement, LagEntryPos);//train fully off old LagElement so can clear TrainOnElement flags - no, reset at earlier call when lag moves off element
1785  if(DerailPending)
1786  // set during last GetLeadElement, but only act on it when train fully on offending point
1787  // i.e. next time Straddle reaches LeadMid
1788  {
1789  Derailed = true;
1790  DerailPending = false;
1794  Utilities->CallLogPop(657);
1795  return;
1796  }
1803  Straddle = MidLag;
1804  // train now fully on the updated Lag & Mid, the front segment is going to move onto the new
1805  // LeadElement during this function (note that if stopped at signal then won't get this far)
1806  if(LeadElement > -1)
1807  {
1809  // i.e an exit continuation only
1810  // if don't exclude entry continuations then can't progress past it
1811  {
1812  LeadElement = -1;
1813  }
1814  else
1815  {
1816  GetLeadElement(0);
1817  // sets or resets DerailPending & StoppedAtSignal, and sets LeadElement values
1819  if(Stopped())
1820  {
1821  if(TrainFailurePending) // ok, stopped so PlotElements set
1822  {
1823  TrainHasFailed(6);
1824  }
1825  Utilities->CallLogPop(658);
1826  return; // i.e. don't move forward one step if next element is a red signal
1827  }
1828  }
1829  }
1830  }
1831  if(LagElement > -1)
1832  {
1833  // below are the actions required at both half moves for LagElement > -1
1835 
1836  // if was in locked route but has timed out when train leaves then plot the normal track graphic over the route graphic that is
1837  // still in BackgroundGraphic[3], if wasn't in a route then will just replot the same BackgroundGraphic
1838  // need to do this for each half element
1839 
1840  TPrefDirElement PrefDirElement;
1841  if(!(AllRoutes->IsElementInLockedRouteGetPrefDirElementGetLockedVectorNumber(7, LagElement, LagExitPos, PrefDirElement, LockedVectorNumber)))
1842  {
1843  int RouteNumber; // holder for call below - not used
1845  {
1846  if(Utilities->clTransparent == TColor(0xFFFFFF))
1847  // change to black for a white background
1848  {
1850  // only applies for AutoSigs Route in case was locked & timed out
1851  }
1852  else
1853  // change to white for a dark background
1854  {
1856  // only applies for AutoSigs Route in case was locked & timed out
1857  }
1859  }
1860  }
1862  // above in case train just moving off a bridge & either alternative track in a route - need to keep its route colour,
1863  // or a train on the opposite track - needs to be replotted
1864  }
1865  // update all array values
1866  HOffset[3] = HOffset[2];
1867  HOffset[2] = HOffset[1];
1868  HOffset[1] = HOffset[0];
1869  VOffset[3] = VOffset[2];
1870  VOffset[2] = VOffset[1];
1871  VOffset[1] = VOffset[0];
1872  Graphics::TBitmap *TempPtr = BackgroundPtr[3];
1873 
1874  BackgroundPtr[3] = BackgroundPtr[2];
1875  BackgroundPtr[2] = BackgroundPtr[1];
1876  BackgroundPtr[1] = BackgroundPtr[0];
1877  BackgroundPtr[0] = TempPtr;
1878 
1879  // update headcode graphics depending on Lead entry value
1880  if(LeadElement > -1) // if Lead is -1 then stays as is
1881  {
1883  {
1884  for(int x = 0; x < 4; x++)
1885  {
1886  HeadCodePosition[x] = HeadCodeGrPtr[3 - x];
1887  }
1888  }
1889  else
1890  {
1891  for(int x = 0; x < 4; x++)
1892  {
1894  }
1895  }
1896  }
1897  if(TrainMode == Timetable)
1898  {
1900  }
1901  else
1902  {
1904  }
1906 
1907  // plot new seg [0] on Lead & [2] on Mid ([2] always on Mid)
1908  if(LeadElement > -1)
1909  {
1910  if(Straddle == MidLag)
1911  // just about to move half onto the new lead element
1912  {
1914  // pick up new background bitmap [0]
1916  int LeadElementTrainID = Track->TrackElementAt(244, LeadElement).TrainIDOnElement;
1917  if((LeadElementTrainID > -1) && (LeadElementTrainID != TrainID))
1918  // check if own ID for entry at continuation, else crashes into itself!
1919  {
1920  // OK if crossing on a bridge
1921  int OtherTrainEntryPos = TrainController->EntryPos(0, LeadElementTrainID, LeadElement);
1922  if(OtherTrainEntryPos == -1)
1923  {
1924  throw Exception("Error - OtherTrainEntryPos not set");
1925  }
1926  if((Track->TrackElementAt(246, LeadElement).TrackType != Bridge) || (LeadEntryPos == OtherTrainEntryPos) ||
1927  // LeadEntryPos for rear end crashes
1928  (LeadExitPos == OtherTrainEntryPos))
1929  // LeadExitPos for head-on crashes
1930  {
1932  Crashed = true; // only set if Straddle = MidLag
1933  CallingOnFlag = false;
1934  // in case was set, need to disable call on if call on button had been pressed
1935  }
1936  }
1937  else if(MidElement > -1) // will be -1 for continuation entries
1938  {
1939  // check if about to move onto a crossing diagonal that is occupied by another train, and if so crash
1940  int MidExitLinkNum = Track->TrackElementAt(889, MidElement).Link[MidExitPos];
1941  int MidHLoc = Track->TrackElementAt(890, MidElement).HLoc;
1942  int MidVLoc = Track->TrackElementAt(891, MidElement).VLoc;
1943  int OtherTrainID = -1;
1944  if((MidExitLinkNum == 1) || (MidExitLinkNum == 3) || (MidExitLinkNum == 7) || (MidExitLinkNum == 9))
1945  {
1946  if(Track->DiagonalFouledByTrain(0, MidHLoc, MidVLoc, MidExitLinkNum, OtherTrainID))
1947  {
1948  TrainCrashedInto = OtherTrainID;
1949  Crashed = true; // only set if Straddle = MidLag
1950  CallingOnFlag = false;
1951  // in case was set, need to disable call on if call on button had been pressed
1952  }
1953  }
1954  }
1955  }
1956  else
1957  {
1959  // pick up new background bitmap [0]
1961  }
1962  PlotElement[0] = LeadElement;
1964  PlotTrainGraphic(12, 0, Display);
1965  }
1966  if(MidElement > -1)
1967  {
1968  PlotElement[2] = MidElement;
1970  PlotTrainGraphic(1, 2, Display);
1971  }
1972  // plot the new positions for [1] & [3] graphics - [1] on Mid if Straddle = MidLag, on Lead if Straddle = LeadMidLag
1973  // [3] on Lag if Straddle = MidLag, on Mid if Straddle = LeadMidLag
1974  if(Straddle == MidLag)
1975  {
1976  if(MidElement > -1)
1977  {
1978  PlotElement[1] = MidElement;
1980  PlotTrainGraphic(2, 1, Display);
1981  }
1982  if(LagElement > -1)
1983  {
1984  PlotElement[3] = LagElement;
1986  PlotTrainGraphic(3, 3, Display);
1987  }
1988  }
1989  else // Straddle == LeadMidLag
1990  {
1991  if(LeadElement > -1)
1992  {
1993  PlotElement[1] = LeadElement;
1995  PlotTrainGraphic(4, 1, Display);
1996  }
1997  if(MidElement > -1)
1998  {
1999  PlotElement[3] = MidElement;
2001  PlotTrainGraphic(5, 3, Display);
2002  }
2003  }
2004  if(Crashed)
2005  // only reach here if crash into another train, if crash into buffers or an LC then return earlier at the if(Stopped()) test
2006  {
2011  // in case was set, need to disable call on if call on button had been pressed
2018  Straddle = LeadMidLag;
2019  // was MidLag but plotted as LeadMidLag so change Straddle accordingly
2020  Display->Update();
2021  // resurrected when Update() dropped from PlotOutput etc
2022  Utilities->CallLogPop(660);
2023  return;
2024  }
2025  // deal here with station stops & pass times after all replotting done but before Straddle changed
2026  if(TrainMode == Timetable)
2027  {
2028  if(Straddle == LeadMidLag)
2029  {
2030  if((LeadElement > -1) && (MidElement > -1) && !TimetableFinished)
2031  {
2032  // NameInTimetableBeforeCDT returns the number by which the train ActionVectorEntryPtr needs to be incremented
2033  // to point to the location arrival entry - before a change of direction
2034  AnsiString LocName = Track->TrackElementAt(249, LeadElement).ActiveTrackElementName;
2035  bool StopRequired = false;
2036  int TTVPos = NameInTimetableBeforeCDT(1, LocName, StopRequired);
2037  if(TTVPos > -1) // -1 if can't find it or if name is ""
2038  {
2039  // check if at buffers (no, dropped buffer check to allow to crash into buffers) or a through station stop,
2040  // or a station where next element contains a train or a stop signal, if so
2041  // stop now, note that for 2nd check, if next element is a bridge then will have stopped by now so no need
2042  // to test the actual track the train is on since it can't be a platform
2043  TTrackElement LeadTrackElement = Track->TrackElementAt(258, LeadElement);
2044  TTrackElement NextTrackElement; // default for now
2045  bool TrainAtStopLinkPos1 = (LeadTrackElement.StationEntryStopLinkPos1 == LeadEntryPos);
2046  bool TrainAtStopLinkPos2 = (LeadTrackElement.StationEntryStopLinkPos2 == LeadEntryPos);
2047  bool ForwardConnection = (LeadTrackElement.Conn[LeadExitPos] > -1);
2048  int NextElementEntryPos = -1;
2049  int NextElementExitPos = -1;
2050  bool TrainOnNextElement = false;
2051  bool StopSignalAtNextElement = false;
2052  if(ForwardConnection)
2053  // if no forward connection can't derive anything from it without errors
2054  {
2055  NextTrackElement = Track->TrackElementAt(262, LeadTrackElement.Conn[LeadExitPos]);
2056  NextElementEntryPos = LeadTrackElement.ConnLinkPos[LeadExitPos];
2057  NextElementExitPos = Track->GetNonPointsOppositeLinkPos(NextElementEntryPos);
2058  // this is only for signals so no need to worry about points ambiguity
2059  TrainOnNextElement = (NextTrackElement.TrainIDOnElement > -1);
2060  StopSignalAtNextElement = ((NextTrackElement.Config[NextElementExitPos] == Signal) && (NextTrackElement.Attribute == 0));
2061  }
2062  // logic here is: if(train@stoplinkpos1 || train@stoplinkpos2 || (forward connection && (train on next element || stop signal at next element)))
2063  if(TrainAtStopLinkPos1 || TrainAtStopLinkPos2 || (ForwardConnection && (TrainOnNextElement || StopSignalAtNextElement)))
2064  {
2065  if(TTVPos > 0)
2066  {
2068  ActionVectorEntryPtr += TTVPos;
2069  }
2070  if(StopRequired)
2071  {
2072  StoppedAtLocation = true;
2073  StoppedAtSignal = false;
2074  // may have been set earlier at line 925 so need to reset as
2075  // StoppedAtLocation takes precedence and don't want both set at same time or have flashing graphic
2076  // in zoom out mode
2077  if(!TrainFailed)
2078  {
2080  // pale green
2081  }
2084  {
2085  TimeTimeLocArrived = true;
2086  // used in case of later signaller control, when need to know
2087  // whether had arrived or not, to avoid sending the arrival
2088  // message twice, see TInterface::TimetableControl1Click
2089  }
2090  }
2091  else
2092  {
2094  }
2096  {
2098  }
2099  // don't alter ActionVectorEntryPtr if at a TimeTimeLoc (& can't be anything else other than TimeLoc or PassTime after calling NameInTimetableBeforeCDT successfully)
2101  }
2102  }
2103  }
2104  }
2105  }
2106  if(Straddle == MidLag)
2107  {
2108  Straddle = LeadMidLag;
2109  FirstHalfMove = false;
2110  }
2111  else if(Straddle == LeadMidLag)
2112  {
2113  Straddle = LeadMid;
2114  FirstHalfMove = true;
2115  }
2116  else if(Straddle == LeadMid)
2117  {
2118  throw Exception("Error, Straddle shouldn't be LeadMid prior to resetting at exit from UpdateTrain");
2119  }
2120  if(TrainFailurePending) // ok, moving but PlotElements set above
2121  {
2122  TrainHasFailed(7);
2123  }
2124  Display->Update();
2125  // need to keep this since Update() not called for PlotSmallOutput as too slow
2126  Utilities->CallLogPop(661);
2127 }
2128 
2129 // ----------------------------------------------------------------------------
2130 
2131 Graphics::TBitmap *TTrain::SetOneGraphicCode(char CodeChar)
2132 {
2133  switch(CodeChar)
2134  {
2135  case '0':
2136  return(RailGraphics->Code0);
2137 
2138  case '1':
2139  return(RailGraphics->Code1);
2140 
2141  case '2':
2142  return(RailGraphics->Code2);
2143 
2144  case '3':
2145  return(RailGraphics->Code3);
2146 
2147  case '4':
2148  return(RailGraphics->Code4);
2149 
2150  case '5':
2151  return(RailGraphics->Code5);
2152 
2153  case '6':
2154  return(RailGraphics->Code6);
2155 
2156  case '7':
2157  return(RailGraphics->Code7);
2158 
2159  case '8':
2160  return(RailGraphics->Code8);
2161 
2162  case '9':
2163  return(RailGraphics->Code9);
2164 
2165  case 'A':
2166  return(RailGraphics->CodeA);
2167 
2168  case 'B':
2169  return(RailGraphics->CodeB);
2170 
2171  case 'C':
2172  return(RailGraphics->CodeC);
2173 
2174  case 'D':
2175  return(RailGraphics->CodeD);
2176 
2177  case 'E':
2178  return(RailGraphics->CodeE);
2179 
2180  case 'F':
2181  return(RailGraphics->CodeF);
2182 
2183  case 'G':
2184  return(RailGraphics->CodeG);
2185 
2186  case 'H':
2187  return(RailGraphics->CodeH);
2188 
2189  case 'I':
2190  return(RailGraphics->CodeI);
2191 
2192  case 'J':
2193  return(RailGraphics->CodeJ);
2194 
2195  case 'K':
2196  return(RailGraphics->CodeK);
2197 
2198  case 'L':
2199  return(RailGraphics->CodeL);
2200 
2201  case 'M':
2202  return(RailGraphics->CodeM);
2203 
2204  case 'N':
2205  return(RailGraphics->CodeN);
2206 
2207  case 'O':
2208  return(RailGraphics->CodeO);
2209 
2210  case 'P':
2211  return(RailGraphics->CodeP);
2212 
2213  case 'Q':
2214  return(RailGraphics->CodeQ);
2215 
2216  case 'R':
2217  return(RailGraphics->CodeR);
2218 
2219  case 'S':
2220  return(RailGraphics->CodeS);
2221 
2222  case 'T':
2223  return(RailGraphics->CodeT);
2224 
2225  case 'U':
2226  return(RailGraphics->CodeU);
2227 
2228  case 'V':
2229  return(RailGraphics->CodeV);
2230 
2231  case 'W':
2232  return(RailGraphics->CodeW);
2233 
2234  case 'X':
2235  return(RailGraphics->CodeX);
2236 
2237  case 'Y':
2238  return(RailGraphics->CodeY);
2239 
2240  case 'Z':
2241  return(RailGraphics->CodeZ);
2242 
2243  case 'a':
2244  return(RailGraphics->Code_a);
2245 
2246  case 'b':
2247  return(RailGraphics->Code_b);
2248 
2249  case 'c':
2250  return(RailGraphics->Code_c);
2251 
2252  case 'd':
2253  return(RailGraphics->Code_d);
2254 
2255  case 'e':
2256  return(RailGraphics->Code_e);
2257 
2258  case 'f':
2259  return(RailGraphics->Code_f);
2260 
2261  case 'g':
2262  return(RailGraphics->Code_g);
2263 
2264  case 'h':
2265  return(RailGraphics->Code_h);
2266 
2267  case 'i':
2268  return(RailGraphics->Code_i);
2269 
2270  case 'j':
2271  return(RailGraphics->Code_j);
2272 
2273  case 'k':
2274  return(RailGraphics->Code_k);
2275 
2276  case 'l':
2277  return(RailGraphics->Code_l);
2278 
2279  case 'm':
2280  return(RailGraphics->Code_m);
2281 
2282  case 'n':
2283  return(RailGraphics->Code_n);
2284 
2285  case 'o':
2286  return(RailGraphics->Code_o);
2287 
2288  case 'p':
2289  return(RailGraphics->Code_p);
2290 
2291  case 'q':
2292  return(RailGraphics->Code_q);
2293 
2294  case 'r':
2295  return(RailGraphics->Code_r);
2296 
2297  case 's':
2298  return(RailGraphics->Code_s);
2299 
2300  case 't':
2301  return(RailGraphics->Code_t);
2302 
2303  case 'u':
2304  return(RailGraphics->Code_u);
2305 
2306  case 'v':
2307  return(RailGraphics->Code_v);
2308 
2309  case 'w':
2310  return(RailGraphics->Code_w);
2311 
2312  case 'x':
2313  return(RailGraphics->Code_x);
2314 
2315  case 'y':
2316  return(RailGraphics->Code_y);
2317 
2318  case 'z':
2319  return(RailGraphics->Code_z);
2320 
2321  default:
2322  return(RailGraphics->TempHeadCode);
2323  }
2324 }
2325 
2326 // ----------------------------------------------------------------------------
2327 
2328 void TTrain::SetHeadCodeGraphics(int Caller, AnsiString Code)
2329 {
2330  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SetHeadCodeGraphics," + HeadCode);
2331  if(Code.Length() != 4)
2332  {
2333  TrainController->StopTTClockMessage(62, "Headcode Incorrect length");
2334  }
2335  for(int x = 1; x < 5; x++) // AnsiString indices start at 1
2336  {
2337  HeadCodeGrPtr[x - 1]->Assign(SetOneGraphicCode(Code[x]));
2338  }
2339  if(BackgroundColour != clB5G5R5)
2340  // i.e. not the basic graphic colour as loaded from resource file
2341  {
2342  for(int x = 0; x < 4; x++)
2343  {
2345  }
2346  }
2347  Utilities->CallLogPop(1484);
2348 }
2349 
2350 // ----------------------------------------------------------------------------
2351 
2352 void TTrain::GetLeadElement(int Caller)
2353 // assumes Mid & Lag already set, sets LeadElement,
2354 // LeadEntryPos, LeadExitPos & DerailPending (don't want to act on it immediately)
2355 {
2356  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",GetLeadElement," + HeadCode);
2357  DerailPending = false;
2361  {
2362  // attr 0=straight, - links 0 & 1 (0 = lead)
2363  // attr 1=diverging, - links 2 & 3 (2 = lead)
2364  // set appropriate next element or derail - use a subroutine & return element & bool for derail
2365  // points always have links 0 & 2 = lead, link 1 = trailing straight, link 3 = training diverging
2366 
2367  // if enter at lead, exit at whatever attr set at
2368  // if enter at lag, exit at lead, but set derail wrt attribute
2369  if((LeadEntryPos == 0) && (Track->TrackElementAt(272, LeadElement).Attribute == 0))
2370  {
2371  LeadExitPos = 1;
2372  }
2373 
2374  // strictly speaking shouldn't need to set to 0 and 2 correctly since TrackIsInARoute caters for both, but
2375  // best to be on safe side
2376  else if(LeadEntryPos == 0)
2377  {
2378  LeadEntryPos = 2;
2379  LeadExitPos = 3;
2380  }
2381  else if((LeadEntryPos == 2) && (Track->TrackElementAt(273, LeadElement).Attribute == 0))
2382  {
2383  LeadEntryPos = 0;
2384  LeadExitPos = 1;
2385  }
2386  else if(LeadEntryPos == 2)
2387  {
2388  LeadExitPos = 3;
2389  }
2390 
2391  else if((LeadEntryPos == 1) && (Track->TrackElementAt(274, LeadElement).Attribute == 0))
2392  {
2393  LeadExitPos = 0;
2394  }
2395  else if(LeadEntryPos == 1)
2396  {
2397  LeadExitPos = 0;
2398  DerailPending = true;
2399  }
2400  else if((LeadEntryPos == 3) && (Track->TrackElementAt(275, LeadElement).Attribute == 0))
2401  {
2402  LeadExitPos = 0;
2403  DerailPending = true;
2404  }
2405  else if(LeadEntryPos == 3)
2406  {
2407  LeadExitPos = 0;
2408  }
2409  }
2410  else if(LeadEntryPos == 0)
2411  {
2412  LeadExitPos = 1;
2413  }
2414  else if(LeadEntryPos == 1)
2415  {
2416  LeadExitPos = 0;
2417  }
2418  else if(LeadEntryPos == 2)
2419  {
2420  LeadExitPos = 3;
2421  }
2422  else if(LeadEntryPos == 3)
2423  {
2424  LeadExitPos = 2;
2425  }
2426  // TTrackElement TrackElement = Track->TrackElementAt(276, LeadElement);
2427 /* signal check moved to Update() function
2428  if((TrackElement.TrackType == SignalPost) && (TrackElement.Config[LeadExitPos] == Signal)
2429  && (TrackElement.Attribute == 0))//0 = red
2430  {
2431  StoppedAtSignal = true; //comment out for test of locked route graphic replot
2432  }
2433  else
2434  {
2435  StoppedAtSignal = false;
2436  }
2437 */
2438  Utilities->CallLogPop(662);
2439 }
2440 
2441 // ----------------------------------------------------------------------------
2442 
2443 void TTrain::GetOffsetValues(int Caller, int &HOffset, int &VOffset, int Link) const
2444 {
2445  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",GetOffsetValues," + AnsiString(Link) + "," + HeadCode);
2446  switch(Link)
2447  {
2448  case 1:
2449  {
2450  HOffset = 0;
2451  VOffset = 0;
2452  break;
2453  }
2454 
2455  case 2:
2456  {
2457  HOffset = 4;
2458  VOffset = 0;
2459  break;
2460  }
2461 
2462  case 3:
2463  {
2464  HOffset = 8;
2465  VOffset = 0;
2466  break;
2467  }
2468 
2469  case 4:
2470  {
2471  HOffset = 0;
2472  VOffset = 4;
2473  break;
2474  }
2475 
2476  case 6:
2477  {
2478  HOffset = 8;
2479  VOffset = 4;
2480  break;
2481  }
2482 
2483  case 7:
2484  {
2485  HOffset = 0;
2486  VOffset = 8;
2487  break;
2488  }
2489 
2490  case 8:
2491  {
2492  HOffset = 4;
2493  VOffset = 8;
2494  break;
2495  }
2496 
2497  case 9:
2498  {
2499  HOffset = 8;
2500  VOffset = 8;
2501  break;
2502  }
2503 
2504  default:
2505  {
2506  throw Exception("Error in GetOffsetValues - Link value wrong");
2507  }
2508  }
2509  Utilities->CallLogPop(674);
2510 }
2511 
2512 // ---------------------------------------------------------------------------
2513 
2514 bool TTrain::LowEntryValue(int EntryLink) const
2515 {
2516 /* returns true if EntryLink is 1, 2, 4 or 7, in these circumstances the front of the train (i.e.
2517  the character that is red or blue) is the last character of the headcode, otherwise it's the first character of the headcode
2518 */
2519  if((EntryLink == 1) || (EntryLink == 2) || (EntryLink == 4) || (EntryLink == 7))
2520  {
2521  return(true);
2522  }
2523  else
2524  {
2525  return(false);
2526  }
2527 }
2528 
2529 // ---------------------------------------------------------------------------
2530 
2531 void TTrain::PickUpBackgroundBitmap(int Caller, int HOffset, int VOffset, int Element, int EntryPos, Graphics::TBitmap *GraphicPtr) const
2532 {
2533  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",PickUpBackgroundBitmap," + AnsiString(HOffset) + "," +
2534  AnsiString(VOffset) + "," + AnsiString(Element) + "," + AnsiString(EntryPos) + "," + HeadCode);
2535  Graphics::TBitmap *EXGraphicPtr = RailGraphics->bmTransparentBgnd;
2536  // default values
2537  Graphics::TBitmap *EntryDirectionGraphicPtr = RailGraphics->bmTransparentBgnd;
2538 
2539  TAllRoutes::TRouteType RouteType;
2540 
2541  RouteType = AllRoutes->GetRouteTypeAndGraphics(11, Element, EntryPos, EXGraphicPtr, EntryDirectionGraphicPtr);
2542 
2543  TRect SourceRect, DestRect;
2544 
2545  DestRect.init(0, 0, 8, 8); // initialise left, top, right, bottom
2546  // note right and bottom rect co-ordinates are 1 greater than the pixel area
2547  SourceRect.init(HOffset, VOffset, HOffset + 8, VOffset + 8);
2548  Graphics::TBitmap *TempGraphic = new Graphics::TBitmap;
2549 
2550  TempGraphic->PixelFormat = pf8bit;
2551  TempGraphic->Width = 16;
2552  TempGraphic->Height = 16;
2553  TTrackElement TempElement = Track->TrackElementAt(286, Element);
2554 
2555  if(TempElement.TrackType == Points)
2556  {
2557  TempGraphic->Assign(TempElement.GraphicPtr);
2558  TempGraphic->Transparent = true;
2559  TempGraphic->TransparentColor = Utilities->clTransparent;
2560  if(RouteType == TAllRoutes::AutoSigsRoute)
2561  {
2562  TempGraphic->Canvas->Draw(0, 0, EXGraphicPtr);
2563  TempGraphic->Canvas->Draw(0, 0, EntryDirectionGraphicPtr);
2564  }
2565  else
2566  {
2567  TempGraphic->Canvas->Draw(0, 0, Track->GetFilletGraphic(1, TempElement)); // add fillet
2568  }
2569  GraphicPtr->Canvas->CopyRect(DestRect, TempGraphic->Canvas, SourceRect);
2570  }
2571  else if(TempElement.TrackType == GapJump) // plot set gap
2572  {
2573  if(TempElement.SpeedTag == 88)
2574  {
2575  TempGraphic->Assign(RailGraphics->gl88set);
2576  }
2577  else if(TempElement.SpeedTag == 89)
2578  {
2579  TempGraphic->Assign(RailGraphics->gl89set);
2580  }
2581  else if(TempElement.SpeedTag == 90)
2582  {
2583  TempGraphic->Assign(RailGraphics->gl90set);
2584  }
2585  else if(TempElement.SpeedTag == 91)
2586  {
2587  TempGraphic->Assign(RailGraphics->gl91set);
2588  }
2589  else if(TempElement.SpeedTag == 92)
2590  {
2591  TempGraphic->Assign(RailGraphics->gl92set);
2592  }
2593  else if(TempElement.SpeedTag == 93)
2594  {
2595  TempGraphic->Assign(RailGraphics->bm93set);
2596  }
2597  else if(TempElement.SpeedTag == 94)
2598  {
2599  TempGraphic->Assign(RailGraphics->bm94set);
2600  }
2601  else if(TempElement.SpeedTag == 95)
2602  {
2603  TempGraphic->Assign(RailGraphics->gl95set);
2604  }
2605  TempGraphic->Transparent = true;
2606  TempGraphic->TransparentColor = Utilities->clTransparent;
2607  if(RouteType == TAllRoutes::AutoSigsRoute)
2608  {
2609  TempGraphic->Canvas->Draw(0, 0, EXGraphicPtr);
2610  TempGraphic->Canvas->Draw(0, 0, EntryDirectionGraphicPtr);
2611  }
2612  GraphicPtr->Canvas->CopyRect(DestRect, TempGraphic->Canvas, SourceRect);
2613  }
2614  // new for version 0.6
2615  else if(TempElement.TrackType == SignalPost)
2616  {
2617  if(TempElement.SigAspect == TTrackElement::GroundSignal)
2618  {
2619  for(int x = 0; x < 40; x++)
2620  {
2621  if((Track->SigTableGroundSignal[x].SpeedTag == TempElement.SpeedTag) && (Track->SigTableGroundSignal[x].Attribute == 0))
2622  // need to stop aspect
2623  {
2624  TempGraphic->Assign(Track->SigTableGroundSignal[x].SigPtr);
2625  break;
2626  }
2627  }
2628  }
2629  else // normal signal
2630  {
2631  TempGraphic->Assign(TempElement.GraphicPtr);
2632  // GraphicPtr set to normal signal in a signal track element
2633  }
2634  TempGraphic->Transparent = true;
2635  TempGraphic->TransparentColor = Utilities->clTransparent;
2636  if(RouteType == TAllRoutes::AutoSigsRoute)
2637  {
2638  TempGraphic->Canvas->Draw(0, 0, EXGraphicPtr);
2639  TempGraphic->Canvas->Draw(0, 0, EntryDirectionGraphicPtr);
2640  }
2641  GraphicPtr->Canvas->CopyRect(DestRect, TempGraphic->Canvas, SourceRect);
2642  }
2643  else
2644  {
2645  // first check if there's a NamedNonStationLocation element at that position & if so pick up that as the background
2646  // can't name points gaps or signals so 'else' OK
2647  bool FoundFlag;
2648  TTrack::TIMPair IMPair = Track->GetVectorPositionsFromInactiveTrackMap(4, TempElement.HLoc, TempElement.VLoc, FoundFlag);
2649  if(FoundFlag)
2650  {
2652  {
2653  GraphicPtr->Canvas->CopyRect(DestRect, Track->InactiveTrackElementAt(26, IMPair.first).GraphicPtr->Canvas, SourceRect);
2654  TempGraphic->Assign(RailGraphics->bmName);
2655  TempGraphic->Transparent = true;
2656  TempGraphic->TransparentColor = Utilities->clTransparent;
2657  if(RouteType == TAllRoutes::AutoSigsRoute)
2658  {
2659  TempGraphic->Canvas->Draw(0, 0, EXGraphicPtr);
2660  TempGraphic->Canvas->Draw(0, 0, EntryDirectionGraphicPtr);
2661  }
2662  else
2663  {
2664  TempGraphic->Canvas->Draw(0, 0, TempElement.GraphicPtr);
2665  }
2666  // draw track on top
2667  GraphicPtr->Canvas->CopyRect(DestRect, TempGraphic->Canvas, SourceRect);
2668  }
2669  else if(Track->InactiveTrackElementAt(116, IMPair.first).TrackType == LevelCrossing)
2670  {
2671  TempGraphic->Assign(TempElement.GraphicPtr);
2672  TempGraphic->Transparent = true;
2673  TempGraphic->TransparentColor = Utilities->clTransparent;
2674  // note that can't be an AutoSigsRoute
2675  // now overlay the LC central portion
2676  int BDVectorPos = -1; //not used
2677  if(Track->AnyLinkedBarrierDownVectorManual(0, Track->InactiveTrackElementAt(130, IMPair.first).HLoc, Track->InactiveTrackElementAt(131, IMPair.first).VLoc, BDVectorPos))
2678  {
2679  TempGraphic->Canvas->Draw(0, 0, RailGraphics->LCPlainMan);
2680  }
2681  else
2682  {
2683  TempGraphic->Canvas->Draw(0, 0, RailGraphics->LCPlain);
2684  }
2685  GraphicPtr->Canvas->CopyRect(DestRect, TempGraphic->Canvas, SourceRect);
2686  }
2687  else
2688  {
2689  TempGraphic->Assign(TempElement.GraphicPtr);
2690  TempGraphic->Transparent = true;
2691  TempGraphic->TransparentColor = Utilities->clTransparent;
2692  if(RouteType == TAllRoutes::AutoSigsRoute)
2693  {
2694  TempGraphic->Canvas->Draw(0, 0, EXGraphicPtr);
2695  TempGraphic->Canvas->Draw(0, 0, EntryDirectionGraphicPtr);
2696  }
2697  GraphicPtr->Canvas->CopyRect(DestRect, TempGraphic->Canvas, SourceRect);
2698  }
2699  }
2700  else
2701  {
2702  TempGraphic->Assign(TempElement.GraphicPtr);
2703  TempGraphic->Transparent = true;
2704  TempGraphic->TransparentColor = Utilities->clTransparent;
2705  if(RouteType == TAllRoutes::AutoSigsRoute)
2706  {
2707  TempGraphic->Canvas->Draw(0, 0, EXGraphicPtr);
2708  TempGraphic->Canvas->Draw(0, 0, EntryDirectionGraphicPtr);
2709  }
2710  GraphicPtr->Canvas->CopyRect(DestRect, TempGraphic->Canvas, SourceRect);
2711  }
2712  }
2713  delete TempGraphic;
2714  Utilities->CallLogPop(675);
2715 }
2716 
2717 // ---------------------------------------------------------------------------
2718 
2719 // This was an attempt to pick up the actual 8x8 graphic from the display, so that text & user graphics would show as soon as the train passed, and overwrite it with the
2720 // reconstructed track, and it works ok but for the little arrows showing route directions at start and end, which extend beyond the track. It doesn't matter for autosig
2721 // routes because they are replotted (alomg with the direction arrows) but for others they shouldn't be. Leave in in case an easy way to remove these pointers comes to mind.
2722 /*
2723 
2724  void TTrain::PickUpBackgroundBitmap(int Caller, int HOffset, int VOffset, int Element, int EntryPos, Graphics::TBitmap *GraphicPtr) const
2725  {
2726  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",PickUpBackgroundBitmap," + AnsiString(HOffset) +
2727  "," + AnsiString(VOffset) + "," + AnsiString(Element) + "," + AnsiString(EntryPos) + "," + HeadCode);
2728  TAllRoutes::TRouteType RouteType;
2729  Graphics::TBitmap *EXGraphicPtr = RailGraphics->bmTransparentBgnd;
2730  // default values
2731  Graphics::TBitmap *EntryDirectionGraphicPtr = RailGraphics->bmTransparentBgnd;
2732  TTrackElement TempElement = Track->TrackElementAt(, Element); //this is a copy of the element passed into the function
2733  TRect SourceRect, DestRect, ScreenSourceRect;
2734  RouteType = AllRoutes->GetRouteTypeAndGraphics(7, Element, EntryPos, EXGraphicPtr, EntryDirectionGraphicPtr);
2735 
2736  DestRect.init(0, 0, 8, 8); //initialise left, top, right, bottom
2737  // note right and bottom rect co-ordinates are 1 greater than the pixel area
2738  SourceRect.init(HOffset, VOffset, HOffset + 8, VOffset + 8);
2739 
2740  //add text & user graphics if any to *GraphicPtr prior to adding the track
2741  int Left = ((TempElement.HLoc - Display->DisplayOffsetH) * 16) + HOffset;
2742  int Top = ((TempElement.VLoc - Display->DisplayOffsetV) * 16) + VOffset;
2743  int Right = Left + 8;
2744  int Bottom = Top + 8;
2745  ScreenSourceRect.init(Left, Top, Right, Bottom);
2746  GraphicPtr->Canvas->CopyMode = cmSrcCopy;
2747  GraphicPtr->Canvas->CopyRect(DestRect, Display->GetImage()->Canvas, ScreenSourceRect);
2748 
2749  Graphics::TBitmap *TempGraphic = new Graphics::TBitmap; //this will hold the 16x16 reconstructed element, prior to transfer of the 8x8 bit to *GraphicPtr
2750  TempGraphic->PixelFormat = pf8bit;
2751  TempGraphic->Width = 16;
2752  TempGraphic->Height = 16;
2753 
2754  Graphics::TBitmap *SourceGraphic = new Graphics::TBitmap; //this will hold the 8x8 element segment from TempGraphic - needed because to keep transparency have to use Draw, not CopyRect
2755  SourceGraphic->PixelFormat = pf8bit;
2756  SourceGraphic->Width = 16;
2757  SourceGraphic->Height = 16;
2758  SourceGraphic->Transparent = true;
2759  SourceGraphic->TransparentColor = Utilities->clTransparent;
2760 
2761  if (TempElement.TrackType == Points)
2762  {
2763  TempGraphic->Assign(TempElement.GraphicPtr);
2764  TempGraphic->Transparent = true;
2765  TempGraphic->TransparentColor = Utilities->clTransparent;
2766  if (RouteType == TAllRoutes::AutoSigsRoute)
2767  {
2768  TempGraphic->Canvas->Draw(0, 0, EXGraphicPtr);
2769  TempGraphic->Canvas->Draw(0, 0, EntryDirectionGraphicPtr);
2770  }
2771  else
2772  {
2773  TempGraphic->Canvas->Draw(0, 0, Track->GetFilletGraphic(1, TempElement)); // add fillet
2774  }
2775  GraphicPtr->Canvas->CopyRect(DestRect, TempGraphic->Canvas, SourceRect);
2776  }
2777  else if (TempElement.TrackType == GapJump) // plot set gap
2778  {
2779  if (TempElement.SpeedTag == 88)
2780  TempGraphic->Assign(RailGraphics->gl88set);
2781  else if (TempElement.SpeedTag == 89)
2782  TempGraphic->Assign(RailGraphics->gl89set);
2783  else if (TempElement.SpeedTag == 90)
2784  TempGraphic->Assign(RailGraphics->gl90set);
2785  else if (TempElement.SpeedTag == 91)
2786  TempGraphic->Assign(RailGraphics->gl91set);
2787  else if (TempElement.SpeedTag == 92)
2788  TempGraphic->Assign(RailGraphics->gl92set);
2789  else if (TempElement.SpeedTag == 93)
2790  TempGraphic->Assign(RailGraphics->bm93set);
2791  else if (TempElement.SpeedTag == 94)
2792  TempGraphic->Assign(RailGraphics->bm94set);
2793  else if (TempElement.SpeedTag == 95)
2794  TempGraphic->Assign(RailGraphics->gl95set);
2795  TempGraphic->Transparent = true;
2796  TempGraphic->TransparentColor = Utilities->clTransparent;
2797  if (RouteType == TAllRoutes::AutoSigsRoute) {
2798  TempGraphic->Canvas->Draw(0, 0, EXGraphicPtr);
2799  TempGraphic->Canvas->Draw(0, 0, EntryDirectionGraphicPtr);
2800  }
2801  GraphicPtr->Canvas->CopyRect(DestRect, TempGraphic->Canvas, SourceRect);
2802  }
2803  // new for version 0.6
2804  else if (TempElement.TrackType == SignalPost)
2805  {
2806  if (TempElement.SigAspect == TTrackElement::GroundSignal)
2807  {
2808  for (int x = 0; x < 40; x++)
2809  {
2810  if ((Track->SigTableGroundSignal[x].SpeedTag == TempElement.SpeedTag) && (Track->SigTableGroundSignal[x].Attribute == 0))
2811  // need to stop aspect
2812  {
2813  TempGraphic->Assign(Track->SigTableGroundSignal[x].SigPtr);
2814  break;
2815  }
2816  }
2817  }
2818  else // normal signal
2819  {
2820  TempGraphic->Assign(TempElement.GraphicPtr);
2821  // GraphicPtr set to normal signal in a signal track element
2822  }
2823  TempGraphic->Transparent = true;
2824  TempGraphic->TransparentColor = Utilities->clTransparent;
2825  if (RouteType == TAllRoutes::AutoSigsRoute) {
2826  TempGraphic->Canvas->Draw(0, 0, EXGraphicPtr);
2827  TempGraphic->Canvas->Draw(0, 0, EntryDirectionGraphicPtr);
2828  }
2829  GraphicPtr->Canvas->CopyRect(DestRect, TempGraphic->Canvas, SourceRect);
2830  }
2831  else {
2832  // first check if there's a NamedNonStationLocation element at that position & if so pick up that as the background
2833  // can't name points gaps or signals so 'else' OK
2834  bool FoundFlag;
2835  TTrack::TIMPair IMPair = Track->GetVectorPositionsFromInactiveTrackMap
2836  (4, TempElement.HLoc, TempElement.VLoc, FoundFlag);
2837  if (FoundFlag)
2838  {
2839  if (Track->InactiveTrackElementAt(, IMPair.first).TrackType == NamedNonStationLocation)
2840  {
2841  GraphicPtr->Canvas->CopyRect(DestRect,
2842  Track->InactiveTrackElementAt(, IMPair.first).GraphicPtr->Canvas, SourceRect);
2843  TempGraphic->Assign(RailGraphics->bmName);
2844  TempGraphic->Transparent = true;
2845  TempGraphic->TransparentColor = Utilities->clTransparent;
2846  if (RouteType == TAllRoutes::AutoSigsRoute)
2847  {
2848  TempGraphic->Canvas->Draw(0, 0, EXGraphicPtr);
2849  TempGraphic->Canvas->Draw(0, 0, EntryDirectionGraphicPtr);
2850  }
2851  else
2852  TempGraphic->Canvas->Draw(0, 0, TempElement.GraphicPtr);
2853  // draw track on top
2854  GraphicPtr->Canvas->CopyRect(DestRect, TempGraphic->Canvas,
2855  SourceRect);
2856  }
2857  else if (Track->InactiveTrackElementAt(, IMPair.first).TrackType == LevelCrossing) {
2858  TempGraphic->Assign(TempElement.GraphicPtr);
2859  TempGraphic->Transparent = true;
2860  TempGraphic->TransparentColor = Utilities->clTransparent;
2861  // note that can't be an AutoSigsRoute
2862  // now overlay the LC central portion
2863  TempGraphic->Canvas->Draw(0, 0, RailGraphics->LCPlain);
2864  GraphicPtr->Canvas->CopyRect(DestRect, TempGraphic->Canvas,
2865  SourceRect);
2866  }
2867  else {
2868  TempGraphic->Assign(TempElement.GraphicPtr);
2869  TempGraphic->Transparent = true;
2870  TempGraphic->TransparentColor = Utilities->clTransparent;
2871  if (RouteType == TAllRoutes::AutoSigsRoute) {
2872  TempGraphic->Canvas->Draw(0, 0, EXGraphicPtr);
2873  TempGraphic->Canvas->Draw(0, 0, EntryDirectionGraphicPtr);
2874  }
2875  GraphicPtr->Canvas->CopyRect(DestRect, TempGraphic->Canvas,
2876  SourceRect);
2877  }
2878  }
2879  else {
2880  TempGraphic->Assign(TempElement.GraphicPtr);
2881  TempGraphic->Transparent = true;
2882  TempGraphic->TransparentColor = Utilities->clTransparent;
2883  if (RouteType == TAllRoutes::AutoSigsRoute) {
2884  TempGraphic->Canvas->Draw(0, 0, EXGraphicPtr);
2885  TempGraphic->Canvas->Draw(0, 0, EntryDirectionGraphicPtr);
2886  }
2887  SourceGraphic->Canvas->CopyRect(DestRect, TempGraphic->Canvas, SourceRect);
2888  GraphicPtr->Canvas->Draw(0, 0, SourceGraphic);
2889  }
2890  }
2891  delete TempGraphic;
2892  delete SourceGraphic;
2893  Utilities->CallLogPop();
2894  }
2895 */
2896 // ---------------------------------------------------------------------------
2897 
2898 void TTrain::PlotTrainGraphic(int Caller, int ArrayNumber, TDisplay *Disp)
2899 {
2900  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",PlotTrainGraphic," + AnsiString(ArrayNumber) + "," + HeadCode);
2901  if(PlotElement[ArrayNumber] == -1)
2902  {
2903  Utilities->CallLogPop(676);
2904  return; // not plotted yet
2905  }
2906  SetTrainElementID(0, PlotElement[ArrayNumber], PlotEntryPos[ArrayNumber]);
2907  // set before plot so gap flashing stops first
2908  Disp->PlotOutput(29, ((Track->TrackElementAt(295, PlotElement[ArrayNumber]).HLoc * 16) + HOffset[ArrayNumber]),
2909  ((Track->TrackElementAt(296, PlotElement[ArrayNumber]).VLoc * 16) + VOffset[ArrayNumber]), HeadCodePosition[ArrayNumber]);
2910  // Only need to set ID for leading element, stays set until train finally leaves the element
2911  Plotted = true;
2912  Utilities->CallLogPop(677);
2913 }
2914 
2915 // ---------------------------------------------------------------------------
2916 
2917 void TTrain::PlotBackgroundGraphic(int Caller, int ArrayNumber, TDisplay *Disp) const
2918 {
2919  Disp->PlotOutput(30, ((Track->TrackElementAt(297, PlotElement[ArrayNumber]).HLoc * 16) + HOffset[ArrayNumber]),
2920  ((Track->TrackElementAt(298, PlotElement[ArrayNumber]).VLoc * 16) + VOffset[ArrayNumber]), BackgroundPtr[ArrayNumber]);
2921 }
2922 
2923 // ---------------------------------------------------------------------------
2924 
2925 bool TTrain::BufferAtExit(int Caller, int Element, int ExitPos) const
2926 {
2927  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",BufferAtExit," + AnsiString(Element) + "," + AnsiString(ExitPos) + "," +
2928  HeadCode);
2929  if((Track->TrackElementAt(299, Element).TrackType == Buffers) && (Track->TrackElementAt(300, Element).Config[ExitPos] == End))
2930  {
2931  Utilities->CallLogPop(678);
2932  return(true);
2933  }
2934  else
2935  {
2936  Utilities->CallLogPop(679);
2937  return(false);
2938  }
2939 }
2940 
2941 // ---------------------------------------------------------------------------
2942 
2943 bool TTrain::ContinuationExit(int Caller, int Element, int ExitPos) const
2944 {
2945  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",ContinuationExit," + AnsiString(Element) + "," + AnsiString(ExitPos) +
2946  "," + HeadCode);
2947  if((Track->TrackElementAt(301, Element).TrackType == Continuation) && (Track->TrackElementAt(302, Element).Config[ExitPos] == End))
2948  {
2949  Utilities->CallLogPop(680);
2950  return(true);
2951  }
2952  else
2953  {
2954  Utilities->CallLogPop(681);
2955  return(false);
2956  }
2957 }
2958 
2959 // ---------------------------------------------------------------------------
2960 
2961 bool TTrain::IsTrainIDOnBridgeTrackPos01(int Caller, unsigned int TrackVectorPosition)
2962 // test whether this train on a bridge on trackpos 0 & 1
2963 {
2964  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",IsTrainIDOnBridgeTrackPos01," + AnsiString(TrackVectorPosition) + "," +
2965  HeadCode);
2966  if(Track->TrackElementAt(303, TrackVectorPosition).TrackType != Bridge)
2967  {
2968  Utilities->CallLogPop(682);
2969  return(false);
2970  }
2971  // if(Track->TrackElementAt(304, TrackVectorPosition).TrainIDOnElement != TrainID) return false; No, if a bridge could be one of 2 TrainIDs
2972  if(Track->TrackElementAt(305, TrackVectorPosition).TrainIDOnBridgeTrackPos01 == TrainID)
2973  {
2974  if(Track->TrackElementAt(306, TrackVectorPosition).TrainIDOnBridgeTrackPos23 == TrainID)
2975  {
2976  throw Exception("Error, same train on two different bridge tracks");
2977  }
2978  else
2979  {
2980  Utilities->CallLogPop(684);
2981  return(true);
2982  }
2983  }
2984  else
2985  {
2986  Utilities->CallLogPop(685);
2987  return(false);
2988  }
2989 }
2990 
2991 // ---------------------------------------------------------------------------
2992 
2993 bool TTrain::IsTrainIDOnBridgeTrackPos23(int Caller, unsigned int TrackVectorPosition)
2994 // test whether this train on a bridge on trackpos 2 & 3
2995 {
2996  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",IsTrainIDOnBridgeTrackPos23," + AnsiString(TrackVectorPosition) + "," +
2997  HeadCode);
2998  if(Track->TrackElementAt(307, TrackVectorPosition).TrackType != Bridge)
2999  {
3000  Utilities->CallLogPop(686);
3001  return(false);
3002  }
3003  // if(Track->TrackElementAt(308, TrackVectorPosition).TrainIDOnElement != TrainID) return false; No, if a bridge could be one of 2 TrainIDs
3004  if(Track->TrackElementAt(309, TrackVectorPosition).TrainIDOnBridgeTrackPos23 == TrainID)
3005  {
3006  // don't carry out check for train on tracks 0 & 1 else will enter an infinite loop if train on both
3007  Utilities->CallLogPop(687);
3008  return(true);
3009  }
3010  else
3011  {
3012  Utilities->CallLogPop(688);
3013  return(false);
3014  }
3015 }
3016 
3017 // ---------------------------------------------------------------------------
3018 
3019 void TTrain::SetTrainElementID(int Caller, unsigned int TrackVectorPosition, int EntryPos)
3020 {
3021  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SetTrainElementID," + AnsiString(TrackVectorPosition) + "," +
3022  AnsiString(EntryPos) + "," + HeadCode);
3023  Track->TrackElementAt(310, TrackVectorPosition).TrainIDOnElement = TrainID;
3024 
3025  // unplot GapFlash graphics if land on flashing gap (this done before train plotted - see PlotTrainGraphic)
3026  if(Track->GapFlashFlag)
3027  {
3029  {
3032  Track->GapFlashFlag = false;
3033  }
3034  }
3035  if(Track->TrackElementAt(311, TrackVectorPosition).TrackType == Bridge)
3036  {
3037  if(EntryPos == -1)
3038  {
3039  throw Exception("Error, TrackVectorPosition set but not EntryPos in SetTrainElementID");
3040  }
3041  if(EntryPos < 2)
3042  {
3043  Track->TrackElementAt(312, TrackVectorPosition).TrainIDOnBridgeTrackPos01 = TrainID;
3044  }
3045  else
3046  {
3047  Track->TrackElementAt(313, TrackVectorPosition).TrainIDOnBridgeTrackPos23 = TrainID;
3048  }
3049  }
3050  Utilities->CallLogPop(690);
3051 }
3052 
3053 // ---------------------------------------------------------------------------
3054 
3055 void TTrain::ResetTrainElementID(int Caller, unsigned int TrackVectorPosition, int EntryPos)
3056 {
3057  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",ResetTrainElementID," + AnsiString(TrackVectorPosition) + "," +
3058  AnsiString(EntryPos) + "," + HeadCode);
3059  if(Track->TrackElementAt(314, TrackVectorPosition).TrackType != Bridge)
3060  {
3061  Track->TrackElementAt(315, TrackVectorPosition).TrainIDOnElement = -1;
3062  }
3063  else
3064  {
3065  if(EntryPos == -1)
3066  {
3067  throw Exception("Error, TrackVectorPosition set but not EntryPos in ResetTrainElementID");
3068  }
3069  if(EntryPos < 2)
3070  {
3071  Track->TrackElementAt(316, TrackVectorPosition).TrainIDOnBridgeTrackPos01 = -1;
3072  }
3073  else
3074  {
3075  Track->TrackElementAt(317, TrackVectorPosition).TrainIDOnBridgeTrackPos23 = -1;
3076  }
3077  if((EntryPos < 2) && (Track->TrackElementAt(318, TrackVectorPosition).TrainIDOnBridgeTrackPos23 > -1))
3078  // i.e. other train on track 2&3
3079  {
3080  Track->TrackElementAt(319, TrackVectorPosition).TrainIDOnElement = Track->TrackElementAt(320, TrackVectorPosition).TrainIDOnBridgeTrackPos23;
3081  }
3082  else if((EntryPos > 1) && (Track->TrackElementAt(321, TrackVectorPosition).TrainIDOnBridgeTrackPos01 > -1))
3083  // i.e. other train on track 1&2
3084  {
3085  Track->TrackElementAt(322, TrackVectorPosition).TrainIDOnElement = Track->TrackElementAt(323, TrackVectorPosition).TrainIDOnBridgeTrackPos01;
3086  }
3087  else
3088  {
3089  Track->TrackElementAt(324, TrackVectorPosition).TrainIDOnElement = -1;
3090  }
3091  }
3092  Utilities->CallLogPop(691);
3093 }
3094 
3095 // ---------------------------------------------------------------------------
3096 
3097 void TTrain::PlotAlternativeTrackRouteGraphic(int Caller, unsigned int ElementVecNum, int ElementEntryPos, int HOffset, int VOffset, TStraddle StraddleValue)
3098 {
3099  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",PlotAlternativeTrackRouteGraphic," + AnsiString(ElementVecNum) + "," +
3100  AnsiString(ElementEntryPos) + "," + AnsiString(HOffset) + "," + AnsiString(VOffset) + "," + AnsiString(StraddleValue) + "," + HeadCode);
3101  int LockedVectorNumber;
3102 
3103  if(Track->TrackElementAt(325, ElementVecNum).TrackType != Bridge)
3104  // && (Track->TrackElementAt(326, ElementVecNum).TrackType != Crossover))
3105  {
3106  // only applies for a bridge as there can't be (or shouldn't be) 2 routes on an element that isn't a bridge
3107  Utilities->CallLogPop(692);
3108  return;
3109  }
3110  if(AllRoutes->TrackIsInARoute(0, ElementVecNum, (3 - ElementEntryPos)))
3111  // i.e other track is in a marked route
3112  // LinkPos doesn't have to be the entry position for the above check
3113  {
3114  TRect SourceRect, DestRect;
3115  DestRect.init(0, 0, 8, 8); // left, top, right, bottom
3116  // note right and bottom rect co-ordinates are 1 greater than the pixel area
3117  SourceRect.init(HOffset, VOffset, HOffset + 8, VOffset + 8);
3118  // identify the route element for the other track
3119  TAllRoutes::TRouteElementPair RoutePair1, RoutePair2;
3120  RoutePair1 = AllRoutes->GetRouteElementDataFromRoute2MultiMap(13, Track->TrackElementAt(327, ElementVecNum).HLoc,
3121  Track->TrackElementAt(328, ElementVecNum).VLoc, RoutePair2);
3122  int FirstELink, SecondELink = -1;
3123  FirstELink = AllRoutes->GetFixedRouteAt(149, RoutePair1.first).GetFixedPrefDirElementAt(159, RoutePair1.second).GetELink();
3124  // must be at least one
3125  if(RoutePair2.first > -1)
3126  {
3127  SecondELink = AllRoutes->GetFixedRouteAt(150, RoutePair2.first).GetFixedPrefDirElementAt(160, RoutePair2.second).GetELink();
3128  }
3129  TPrefDirElement RouteElement;
3130  // Graphics::TBitmap *RouteGraphic;
3131  if(FirstELink == Track->TrackElementAt(329, ElementVecNum).Link[ElementEntryPos])
3132  // i.e. other track is in RoutePair2
3133  {
3134  if(SecondELink == -1)
3135  {
3136  throw Exception("Error - Second ELink should be set but isn't in PlotAlternativeTrackRouteGraphic [1]");
3137  }
3138  if(SecondELink == Track->TrackElementAt(330, ElementVecNum).Link[ElementEntryPos])
3139  // error if both have same Link number
3140  {
3141  throw Exception("Error - First & Second ELinks have same value in PlotAlternativeTrackRouteGraphic");
3142  }
3143  // RouteGraphic = AllRoutes->GetFixedRouteAt(151, RoutePair2.first).GetFixedPrefDirElementAt(161, RoutePair2.second).GetEXGraphicPtr();
3144  RouteElement = AllRoutes->GetFixedRouteAt(152, RoutePair2.first).GetFixedPrefDirElementAt(162, RoutePair2.second);
3145  }
3146  else // other track is in RoutePair1
3147  {
3148  // RouteGraphic = AllRoutes->GetFixedRouteAt(153, RoutePair1.first).GetFixedPrefDirElementAt(163, RoutePair1.second).GetEXGraphicPtr();
3149  RouteElement = AllRoutes->GetFixedRouteAt(154, RoutePair1.first).GetFixedPrefDirElementAt(164, RoutePair1.second);
3150  }
3151  Graphics::TBitmap *DestGraphic = new Graphics::TBitmap;
3152  DestGraphic->PixelFormat = pf8bit;
3153  DestGraphic->Width = 8;
3154  DestGraphic->Height = 8;
3155  DestGraphic->Transparent = true;
3156  // has to be transparent or will overwrite the track that the train has just left
3157  DestGraphic->TransparentColor = Utilities->clTransparent;
3158  DestGraphic->Canvas->CopyRect(DestRect, RouteElement.GetRouteEXGraphicPtr()->Canvas, SourceRect);
3159  Display->PlotOutput(31, (Track->TrackElementAt(331, ElementVecNum).HLoc * 16) + HOffset,
3160  (Track->TrackElementAt(332, ElementVecNum).VLoc * 16) + VOffset, DestGraphic);
3161  // plot locked route marker for other route if appropriate
3162  TPrefDirElement PrefDirElement; // holder for next call, unused
3163  // plot locked route marker if appropriate, but only when train leaves element completely as this is a 16x16 graphic
3164  if(StraddleValue == LeadMidLag)
3165  {
3167  PrefDirElement, LockedVectorNumber))
3168  {
3169  Display->PlotOutput(32, (Track->TrackElementAt(333, ElementVecNum).HLoc * 16), (Track->TrackElementAt(334, ElementVecNum).VLoc * 16),
3170  RailGraphics->LockedRouteCancelPtr[RouteElement.GetELink()]);
3171  }
3172  }
3173  delete DestGraphic;
3174  }
3175  // but - there may be a train on the other track - if so need to replot it else the section of route overwrites it
3176  // also can only be a bridge or trains either have already or soon will crash
3177  if(Track->TrackElementAt(335, ElementVecNum).TrackType != Bridge)
3178  {
3179  Utilities->CallLogPop(695);
3180  return;
3181  }
3182  if(ElementEntryPos > 1) // other train is on track 01
3183  {
3184  if(Track->TrackElementAt(336, ElementVecNum).TrainIDOnBridgeTrackPos01 > -1)
3185  {
3187  }
3188  }
3189  else // other train is on track 23
3190  {
3191  if(Track->TrackElementAt(338, ElementVecNum).TrainIDOnBridgeTrackPos23 > -1)
3192  {
3194  }
3195  }
3196  Utilities->CallLogPop(696);
3197 }
3198 
3199 // ---------------------------------------------------------------------------
3200 
3201 void TTrain::CheckAndCancelRouteForWrongEndEntry(int Caller, int Element, int EntryPos)
3202 {
3203  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CheckAndCancelRouteForWrongEndEntry," + AnsiString(Element) + "," +
3204  AnsiString(EntryPos) + "," + HeadCode);
3205  int RouteNumber;
3206  bool WrongRoute = false;
3207  TPrefDirElement RouteElement;
3209  TAllRoutes::TRoute2MultiMapIterator Route2MultiMapIterator;
3210 
3211  if(AllRoutes->GetRouteTypeAndNumber(11, Element, EntryPos, RouteNumber) == TAllRoutes::NoRoute)
3212  // here if single track element & no route, or double track element with no route at EntryPos but still need to check if on points or a crossover on non-route track,
3213  // and force-erase route if so (bridge OK of course) note that GetRouteTypeAndNumber allows for points having an EntryPos of 0 or 2 & still returns correct values
3214  {
3215  if((Track->TrackElementAt(340, Element).TrackType == Crossover) || (Track->TrackElementAt(341, Element).TrackType == Points))
3216  {
3217  if(AllRoutes->GetRouteTypeAndNumber(12, Element, (3 - EntryPos), RouteNumber) != TAllRoutes::NoRoute)
3218  // (3-EntryPos) guarantees other route (0->3; 1->2; 2->1; 3->0)
3219  {
3220  if(AllRoutes->GetFixedRouteAt(179, RouteNumber).PrefDirSize() > 2)
3221  {
3222  // don't call for stub end routes
3224  }
3225  AllRoutes->GetModifiableRouteAt(13, RouteNumber).ForceCancelRoute(1);
3226  Utilities->CallLogPop(697);
3227  return;
3228  }
3229  }
3230  // also need to check for a route on a crossing diagonal
3231  TTrackElement TrackElement = Track->TrackElementAt(892, Element);
3232  int LinkNumber = TrackElement.Link[EntryPos];
3233  if((LinkNumber == 1) || (LinkNumber == 3) || (LinkNumber == 7) || (LinkNumber == 9))
3234  {
3235  if(AllRoutes->DiagonalFouledByRoute(0, TrackElement.HLoc, TrackElement.VLoc, LinkNumber))
3236  {
3237  // for LinkNumber = 1, potentially fouled diagonals are at H-1, V, Lk 3 & H, V-1, Lk 7
3238  bool LogActionErrorCalled = false;
3239  // to ensure only called once if have 2 routes on the 2 crossed diagonals
3240  if(LinkNumber == 1)
3241  {
3242  if(AllRoutes->FindRouteNumberFromRoute2MultiMapNoErrors(0, TrackElement.HLoc - 1, TrackElement.VLoc, 3, RouteNumber))
3243  {
3244  if(AllRoutes->GetFixedRouteAt(207, RouteNumber).PrefDirSize() > 2)
3245  {
3246  // don't call for stub end routes
3248  LogActionErrorCalled = true;
3249  }
3250  AllRoutes->GetModifiableRouteAt(20, RouteNumber).ForceCancelRoute(3);
3251  }
3252  if(AllRoutes->FindRouteNumberFromRoute2MultiMapNoErrors(1, TrackElement.HLoc, TrackElement.VLoc - 1, 7, RouteNumber))
3253  // not else in case have different routes on each diagonal, though shouldn't be possible
3254  {
3255  if(!LogActionErrorCalled && AllRoutes->GetFixedRouteAt(208, RouteNumber).PrefDirSize() > 2)
3256  {
3257  // don't call for stub end routes
3259  }
3260  AllRoutes->GetModifiableRouteAt(21, RouteNumber).ForceCancelRoute(4);
3261  }
3262  }
3263 
3264  // for LinkNumber = 3, potentially fouled diagonals are at H+1, V, Lk 1 & H, V-1 Lk 9
3265  else if(LinkNumber == 3)
3266  {
3267  if(AllRoutes->FindRouteNumberFromRoute2MultiMapNoErrors(2, TrackElement.HLoc + 1, TrackElement.VLoc, 1, RouteNumber))
3268  {
3269  if(AllRoutes->GetFixedRouteAt(209, RouteNumber).PrefDirSize() > 2)
3270  {
3271  // don't call for stub end routes
3273  LogActionErrorCalled = true;
3274  }
3275  AllRoutes->GetModifiableRouteAt(22, RouteNumber).ForceCancelRoute(5);
3276  }
3277  if(AllRoutes->FindRouteNumberFromRoute2MultiMapNoErrors(3, TrackElement.HLoc, TrackElement.VLoc - 1, 9, RouteNumber))
3278  // not else in case have different routes on each diagonal, though shouldn't be possible
3279  {
3280  if(!LogActionErrorCalled && AllRoutes->GetFixedRouteAt(210, RouteNumber).PrefDirSize() > 2)
3281  {
3282  // don't call for stub end routes
3284  }
3285  AllRoutes->GetModifiableRouteAt(23, RouteNumber).ForceCancelRoute(6);
3286  }
3287  }
3288 
3289  // for LinkNumber = 7, potentially fouled diagonals are at H-1, V, Lk 9 & H, V+1 Lk 1
3290  else if(LinkNumber == 7)
3291  {
3292  if(AllRoutes->FindRouteNumberFromRoute2MultiMapNoErrors(4, TrackElement.HLoc - 1, TrackElement.VLoc, 9, RouteNumber))
3293  {
3294  if(AllRoutes->GetFixedRouteAt(211, RouteNumber).PrefDirSize() > 2)
3295  {
3296  // don't call for stub end routes
3298  LogActionErrorCalled = true;
3299  }
3300  AllRoutes->GetModifiableRouteAt(24, RouteNumber).ForceCancelRoute(7);
3301  }
3302  if(AllRoutes->FindRouteNumberFromRoute2MultiMapNoErrors(5, TrackElement.HLoc, TrackElement.VLoc + 1, 1, RouteNumber))
3303  // not else in case have different routes on each diagonal, though shouldn't be possible
3304  {
3305  if(!LogActionErrorCalled && AllRoutes->GetFixedRouteAt(212, RouteNumber).PrefDirSize() > 2)
3306  {
3307  // don't call for stub end routes
3309  }
3310  AllRoutes->GetModifiableRouteAt(25, RouteNumber).ForceCancelRoute(8);
3311  }
3312  }
3313 
3314  // for LinkNumber = 9, potentially fouled diagonals are at H+1, V, Lk 7 & H, V+1 Lk 3
3315  else if(LinkNumber == 9)
3316  {
3317  if(AllRoutes->FindRouteNumberFromRoute2MultiMapNoErrors(6, TrackElement.HLoc + 1, TrackElement.VLoc, 7, RouteNumber))
3318  {
3319  if(AllRoutes->GetFixedRouteAt(213, RouteNumber).PrefDirSize() > 2)
3320  {
3321  // don't call for stub end routes
3323  LogActionErrorCalled = true;
3324  }
3325  AllRoutes->GetModifiableRouteAt(26, RouteNumber).ForceCancelRoute(9);
3326  }
3327  if(AllRoutes->FindRouteNumberFromRoute2MultiMapNoErrors(7, TrackElement.HLoc, TrackElement.VLoc + 1, 3, RouteNumber))
3328  // not else in case have different routes on each diagonal, though shouldn't be possible
3329  {
3330  if(!LogActionErrorCalled && AllRoutes->GetFixedRouteAt(214, RouteNumber).PrefDirSize() > 2)
3331  {
3332  // don't call for stub end routes
3334  }
3335  AllRoutes->GetModifiableRouteAt(27, RouteNumber).ForceCancelRoute(10);
3336  }
3337  }
3338  }
3339  }
3340  Utilities->CallLogPop(698);
3341  return; // no route on other track or no other track
3342  }
3343  // here if there is a route at Element & EntryPos - so there can't be a route on the other track if a 4 track element, unless it's a bridge & that's ok
3344  for(unsigned int x = 0; x < AllRoutes->GetFixedRouteAt(155, RouteNumber).PrefDirSize(); x++)
3345  {
3346  RouteElement = AllRoutes->GetFixedRouteAt(156, RouteNumber).GetFixedPrefDirElementAt(165, x);
3347  bool PointsAtElement = (Track->TrackElementAt(987, Element).TrackType == Points); // new at v2.4.2 for points check - Xeon repoted it 30/05/20. He found that for routes that
3348  if(RouteElement.GetTrackVectorPosition() == (unsigned int)Element) // cross bridges at both levels can have entrypos 0 & other exitpos 2 so if don't have this check can cancel a route wrongly
3349  {
3350  if(RouteElement.GetELinkPos() == EntryPos)
3351  {
3352  Utilities->CallLogPop(699);
3353  return; // right direction
3354  }
3355  else if((RouteElement.GetELinkPos() == 2) && (EntryPos == 0) && PointsAtElement)
3356  {
3357  Utilities->CallLogPop(700);
3358  return; // right direction (points)
3359  }
3360  else if((RouteElement.GetELinkPos() == 0) && (EntryPos == 2) && PointsAtElement)
3361  {
3362  Utilities->CallLogPop(701);
3363  return; // right direction (points)
3364  }
3365  else if(RouteElement.GetXLinkPos() == EntryPos)
3366  {
3367  WrongRoute = true;
3368  break; // wrong direction
3369  }
3370  else if((RouteElement.GetXLinkPos() == 2) && (EntryPos == 0) && PointsAtElement) // ok for bridges
3371  {
3372  WrongRoute = true;
3373  break; // wrong direction
3374  }
3375  else if((RouteElement.GetXLinkPos() == 0) && (EntryPos == 2) && PointsAtElement) // ok for bridges
3376  {
3377  WrongRoute = true;
3378  break; // wrong direction
3379  }
3380  }
3381  }
3382  if(!WrongRoute)
3383  {
3384  throw Exception("Error, Element in route but no route found in CheckAndCancelRouteForWrongEndEntry");
3385  }
3386  if(AllRoutes->GetFixedRouteAt(180, RouteNumber).PrefDirSize() > 2)
3387  {
3388  // don't call for stub end routes
3390  }
3391  AllRoutes->GetModifiableRouteAt(14, RouteNumber).ForceCancelRoute(2);
3392  Utilities->CallLogPop(703);
3393 }
3394 
3395 // ---------------------------------------------------------------------------
3396 
3397 void TTrain::PlotTrainWithNewBackgroundColour(int Caller, TColor NewBackgroundColour, TDisplay *Disp)
3398 {
3399  if(BackgroundColour == NewBackgroundColour)
3400  {
3401  return; // don't replot if already correct
3402 
3403  }
3404  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",PlotTrainWithNewBackgroundColour," + AnsiString(NewBackgroundColour));
3405  bool ColourError = false, ColourError2 = false;
3406 
3407  RailGraphics->ChangeBackgroundColour(1, FrontCodePtr, FrontCodePtr, NewBackgroundColour, BackgroundColour, ColourError);
3408  if(ColourError)
3409  {
3410  ColourError2 = true;
3411  }
3412  for(int x = 0; x < 4; x++)
3413  {
3414  RailGraphics->ChangeBackgroundColour(2, HeadCodeGrPtr[x], HeadCodeGrPtr[x], NewBackgroundColour, BackgroundColour, ColourError);
3415  if(ColourError)
3416  {
3417  ColourError2 = true;
3418  }
3419  }
3420  if(ColourError2)
3421  {
3423  "ERROR: Colour depth insufficient to display train colours properly. Please ensure that the 'safe' (web) palette of 256 colours can be displayed");
3424  }
3425  // NB need a separate 'for' loop since the plot order can be different from the graphic order depending on the direction
3426  // of motion
3427  for(int x = 0; x < 4; x++)
3428  {
3429  PlotTrainGraphic(6, x, Disp);
3430  }
3431  BackgroundColour = NewBackgroundColour;
3432  Display->Update();
3433  // need to keep this since Update() not called for PlotSmallOutput as too slow
3434  Utilities->CallLogPop(704);
3435 }
3436 
3437 // ---------------------------------------------------------------------------
3438 
3439 void TTrain::SetTrainMovementValues(int Caller, int TrackVectorPosition, int EntryPos)
3440 /*
3441 Note: Within the loop BrakeRate can only increase and MaxExitSpeed can only reduce
3442 
3443 Summary: Called during PlotStartPosition to set initial values, when stopped and need to restart, and during UpdateTrain when Straddle is LeadMidLag,
3444 i.e. just as the front of a train is about to move fully onto an element, where TrackVectorPosition is the element immediately in front
3445 of the element the front of the train is moving fully on to. The function calculates the times and speeds at the next half-element and
3446 full-element moves.
3447 
3448 Detail: TrackVectorPosition & EntryPos correspond to the TrackVector element immediately in front of where the train is at
3449 the end of the current Update(). EntrySpeed is needed but this is a class data member so isn't passed in. Set the
3450 train BrakeRate to zero (for now, likely to be altered later), & check if zero entry speed with another train directly in front & if so
3451 remain stopped. Pick up the half length value and speed limit for the EntryPos track, and set FrontElementLength to the length of the
3452 EntryPos track, then set LimitingSpeed to the minimum of the element speed limit or the train's maximum speed. Check if running past a
3453 red signal and set SPADFlag if so (use 1 for EntrySpeed rather than 0 as this value is a double so could be slightly in excess of 0).
3454 In this case set the brake rate to maximum to stop as soon as possible.
3455 
3456 For no SPAD calculate the distance that will be travelled at the maximum speed at which the train can exit the next element at half
3457 MaxBrakeRate, this is DistanceAtHalfBraking (also calculate DistanceAtThreeQuarterBraking - used for stopping under signaller control).
3458 DistanceAtHalfBraking is used as the limiting forward look from the next element (i.e. following EntryPos)
3459 for computing the actual braking rate. If no more restrictive speed limits or reasons to stop are found within the forward look then the
3460 train can accelerate or stay at its (local) maximum speed for the next element. The maximum speed on exit from the next element is used
3461 for calculating the forward look because it represents the worst case - i.e. assumes that the train accelerates for the next element.
3462 
3463 A loop is now entered where the CumulativeLength is updated and each successive element (if there are any - current element checked
3464 first to see whether buffers or continuation) in turn is examined: first the length of the
3465 current element is added to the cumulative length; then the half length and speedlimit are set for the next element - points are
3466 followed according to their current setting (Attribute), but derailments are ignored as these are dealt with outside this function; checks
3467 are then made to see whether the next element is a red signal (train should stop before it); next element is a buffer (train should stop
3468 at the end of it so the cumulative length has the next element length added); current element is a buffer (train should stop
3469 at the end of the current element so no need to alter the cumulative length); or have reached a named location stop position. For any of
3470 these reasons, or if stopping under signaller control, there is no more looping, instead the braking rate is calculated to bring the train
3471 to a stop over CumulativeLength. For all normal purposes the braking rate will be less than half (light braking), or less than three
3472 quarters if stopping under signaller control (heavy braking). However if signals are reset in front of a train then the train may need
3473 emergency braking (> 90% max brake rate) and a SPAD may result. Similarly if points are chaged in front of a train that divert it into a
3474 siding then again emeregency braking may be necessary and a crash may result.
3475 
3476 If the train is due to stop then the function calculates the half and full times and speeds and returns. However the calculation depends
3477 on the conditions at entry. If the EntrySpeed is lower than MaxHalfSpeed and the EntryPos element is the one
3478 that the train has to stop at the end of, as it might be for example if train had been stopped at a signal and the next element is a
3479 buffer, then the train accelerates for half the element and brakes for the other half.
3480 Now the BrakeRate is calculated (limited to the MaxBrakeRate), but if it is less than a value calculated at an earlier pass round
3481 the loop then it retains its earlier value (may be due to a close speed restriction that requires more braking than a more distant stop
3482 requirement). The MaxExitSpeedAtHalfBraking (maximum speed at which the train can leave the current element and still stop when required
3483 at half the max braking rate) value is also calculated using EntrySpeed and CumulativeLength, but limiting it to the line speed limit or
3484 train MaxRunningSpeed whichever is the lower. If EntrySpeed > MaxExitSpeedAtHalfBraking then braking is required, so the half and full
3485 speed and time values for the current element are calculated using BrakeRate, EntrySpeed and CurrentElementHalfLength. If need to stop
3486 at the end of the current elemecumulativent for other than a red signal (SPADs can occur) then ExitSpeedFull is set to 0. It should be calculated
3487 as 0 anyway for other than a red signal but this makes sure. If EntrySpeed <= MaxExitSpeedAtHalfBraking then can calculate the half and
3488 full speed and time values for acceleration over the current element, but limit ExitSpeedHalf & Full to MaxExitSpeedAtHalfBraking or to
3489 the current element speed limit if necessary. Check whether ExitSpeedHalf <= EntrySpeed (+0.01 since it's a double) and use constant speed
3490 time values for Half & Full if so, but prior to this increase EntrySpeed if necessary to avoid a divide by zero error.
3491 
3492 If the train is not due to stop within the DistanceAtHalfBraking from the next element following EntryPos then the next element (if there
3493 is one) is checked to see if its speed limit is less than the current value of LimitingSpeed (which is the minimum of any earlier element's
3494 speed limit that has been examined within the loop and the train's MaxRunning speed), and if so LimitingSpeed is set down to it. Now
3495 the MaxExitSpeedAtHalfBraking is calculated, limiting it to LimitingSpeed if less, in case need to accelerate in the current element, in
3496 which case the exit speeds need to be limited to MaxExitSpeedAtHalfBraking. If EntrySpeed > LimitingSpeed then calculate the braking rate
3497 to bring the speed down to LimitingSpeed in CumulativeLength, keeping the existing BrakeRate value if lower and keeping it within
3498 MaxBrakeRate.
3499 
3500 Then, providing the current element isn't a buffer or continuation, the 'Current' values are updated from the 'Next' values ready for
3501 the next loop iteration. The loop is broken out of if the current element is a buffer or continuation, the next element is a
3502 continuation, or (CumulativeLength - FrontElementLength) >= DistanceAtHalfBraking.
3503 
3504 Now the final Half and Full values can be set for braking (if BrakeRate > 0.01), or accelerating - limiting the half and full exit speed
3505 values to MaxExitSpeedAtHalfBraking if necessary, and using constant speed time values if the exit speeds aren't much different to
3506 EntrySpeed and EntrySpeed > 0.01 (to avoid a divide by zero error).
3507 
3508 Note that in no circumstances will a train stop when straddling 3 elements, it will always be fully on two elements. This is ensured
3509 by UpdateTrain() which never sets any stop conditions unless the train is fully on 2 elements when that function returns, i.e. entered
3510 when Straddle == LeadMidLag
3511 */
3512 {
3513  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SetTrainMovementValues," + AnsiString(TrackVectorPosition) + "," +
3514  AnsiString(EntryPos) + "," + HeadCode);
3515  int EntryHalfLength, CurrentElementHalfLength, NextElementHalfLength, CumulativeLength = 0, CurrentTrackVectorPosition = TrackVectorPosition;
3516  int DistanceAtHalfBraking, DistanceAtThreeQuarterBraking, ExitPos, NextTrackVectorPosition, NextEntryPos;
3517  bool RedSignalFlag = false, BuffersFlag = false, StationFlag = false, BuffersOrContinuationNowFlag = false, ContinuationNextFlag = false,
3518  TrainInFrontInSignallerModeFlag = false;
3519  double LimitingSpeed, FrontElementMaxSpeed, MaxExitSpeedAtHalfBrakingSquared, MaxExitSpeedAtHalfBraking, NextSpeedLimit, TempBrakeRate;
3520  double ExitSpeedHalfSquared, ExitSpeedFullSquared;
3521  bool SignallerStopRequired = false;
3522 
3524  // set high to begin with to avoid divide by zero errors on restart after stops, will be set lower later
3525 
3526  // Member variables: EntrySpeed, ExitSpeedHalf, ExitSpeedFull, MaxExitSpeed, BrakeRate, EntryTime, ExitTimeHalf, ExitTimeFull, FrontElementSpeedLimit, FrontElementLength;
3527 
3528  OneLengthAccelDecel = false;
3529  BrakeRate = 0;
3530 
3531 //find FrontElementLength & FrontElementSpeedLimit (these correspond to TrackVectorPosition input value);
3532  if(CurrentTrackVectorPosition > -1)
3533  {
3534  if(Track->TrackElementAt(855, CurrentTrackVectorPosition).TrackType == Points) // this test & section added at v0.6
3535  {
3536  if((EntryPos == 0) || (EntryPos == 2))
3537  {
3538  if(Track->TrackElementAt(856, CurrentTrackVectorPosition).Attribute == 0)
3539  {
3540  CurrentElementHalfLength = (Track->TrackElementAt(857, CurrentTrackVectorPosition).Length01) / 2;
3541  FrontElementSpeedLimit = Track->TrackElementAt(858, CurrentTrackVectorPosition).SpeedLimit01;
3542  }
3543  else
3544  {
3545  CurrentElementHalfLength = (Track->TrackElementAt(859, CurrentTrackVectorPosition).Length23) / 2;
3546  FrontElementSpeedLimit = Track->TrackElementAt(860, CurrentTrackVectorPosition).SpeedLimit23;
3547  }
3548  }
3549  else if(EntryPos == 1)
3550  {
3551  CurrentElementHalfLength = (Track->TrackElementAt(861, CurrentTrackVectorPosition).Length01) / 2;
3552  FrontElementSpeedLimit = Track->TrackElementAt(862, CurrentTrackVectorPosition).SpeedLimit01;
3553  }
3554  else // == 3
3555  {
3556  CurrentElementHalfLength = (Track->TrackElementAt(863, CurrentTrackVectorPosition).Length23) / 2;
3557  FrontElementSpeedLimit = Track->TrackElementAt(864, CurrentTrackVectorPosition).SpeedLimit23;
3558  }
3559  }
3560  else
3561  {
3562  if(EntryPos > 1)
3563  {
3564  CurrentElementHalfLength = (Track->TrackElementAt(348, CurrentTrackVectorPosition).Length23) / 2;
3565  FrontElementSpeedLimit = Track->TrackElementAt(349, CurrentTrackVectorPosition).SpeedLimit23;
3566  }
3567  else
3568  {
3569  CurrentElementHalfLength = (Track->TrackElementAt(350, CurrentTrackVectorPosition).Length01) / 2;
3570  FrontElementSpeedLimit = Track->TrackElementAt(351, CurrentTrackVectorPosition).SpeedLimit01;
3571  }
3572  }
3573  EntryHalfLength = CurrentElementHalfLength;
3574  FrontElementLength = 2 * CurrentElementHalfLength;
3575  }
3576  else
3577  {
3578  throw Exception("Error - CurrentTrackVectorPosition < 0 in SetTrainMovementValues");
3579  }
3580  if((CurrentElementHalfLength < 0) || (FrontElementSpeedLimit < 0))
3581  {
3582  throw Exception("Error - HalfLength or SpeedLimit < 0 in SetTrainMovementValues");
3583  }
3584  // check if zero entry speed with another train directly in front & if so remain stopped
3585  if(Track->OtherTrainOnTrack(2, CurrentTrackVectorPosition, EntryPos, TrainID) && (EntrySpeed < 1))
3586  {
3587  EntrySpeed = 0;
3588  ExitSpeedHalf = 0;
3589  ExitSpeedFull = 0;
3590  MaxExitSpeed = 0;
3591  BrakeRate = 0;
3592  ExitTimeHalf = EntryTime + TDateTime(1/24); //set this high in case used later though unlikely
3593  ExitTimeFull = EntryTime + TDateTime(1/23); //set about 2.5 mins later than half time
3594  StoppedForTrainInFront = true;
3595  Utilities->CallLogPop(705);
3596  return;
3597  }
3598  // new at v2.4.0 - check for stopped and zero power
3599  if((EntrySpeed < 1) && PowerAtRail < 1)
3600  {
3601  EntrySpeed = 0;
3602  ExitSpeedHalf = 0;
3603  ExitSpeedFull = 0;
3604  MaxExitSpeed = 0;
3605  BrakeRate = 0;
3606  ExitTimeHalf = EntryTime + TDateTime(1/24); //set this high in case used later though unlikely
3607  ExitTimeFull = EntryTime + TDateTime(1/23); //set about 2.5 mins later than half time
3608  StoppedWithoutPower = true;
3609  Utilities->CallLogPop(2125);
3610  return;
3611  }
3612 //set LimitingSpeed & FrontElementMaxSpeed (internal values)
3613  if(BeingCalledOn)
3614  {
3615  LimitingSpeed = CallOnMaxSpeed;
3616  }
3617  else
3618  {
3619  LimitingSpeed = MaximumSpeedLimit;
3620  }
3621  if(LimitingSpeed > FrontElementSpeedLimit)
3622  {
3623  LimitingSpeed = FrontElementSpeedLimit;
3624  }
3625  if(LimitingSpeed > MaxRunningSpeed) //MaxRunningSpeed is set in AddTrain depending on timetable or signaller control mode
3626  {
3627  LimitingSpeed = MaxRunningSpeed;
3628  }
3629  FrontElementMaxSpeed = LimitingSpeed;
3630 
3631 /*
3632  for braking the deceleration rate is constant so the following formuli (Newton's Laws) are used:-
3633  (1) V^2/(3.6^2) = U^2/(3.6^2) - 2FS;
3634  (2) V/3.6 = U/3.6 - FT;
3635  (3) S = UT/3.6 - 0.5FT^2
3636  where(V = final speed in kph [km/h/3.6 = m/s], U = initial speed in km/h, F = deceleration rate in m/s/s, S = distance in m & T = time in secs)
3637 
3638  for accelerating the energy input rate (PowerAtRail) is constant so the following formuli are used:-
3639  (4) V^2/(3.6^2) - U^2/(3.6^2) = A^2T;
3640  (5) V = 3.6 * ((1.5*S*A^2) + U^3/ (3.6)^3)^0.333334;
3641  where A is a constant (2*PowerAtRail/Mass)^0.5; V = final speed in kph, U = initial speed in kph , S = distance in m & T = time in secs
3642  It's a bit unrealistic during the early acceleration phase as it will be too rapid, but shouldn't affect the running unduly
3643 
3644  calc max speed that can attain on exit from next element (as could accelerate over next element) and use that speed to calc
3645  DistanceAtHalfBraking, if use actual speed may miss a stop requirement just outside look-ahead & accelerate, and at next calc
3646  be unable to stop or have hard acceleration followed immediately by hard braking, this speed makes for smoother operation
3647 */
3648 
3649 // check if running past a red signal without permission
3650  if((Track->TrackElementAt(352, CurrentTrackVectorPosition).Config[Track->GetNonPointsOppositeLinkPos(EntryPos)] == Signal) && (Track->TrackElementAt(353,
3651  CurrentTrackVectorPosition).Attribute == 0) && (EntrySpeed > 1) && !AllowedToPassRedSignal)
3652  {
3653  SPADFlag = true; // user has to intervene to reset & restart after spad
3654  }
3655  if(!SPADFlag)
3656  {
3657  ExitSpeedFull = 3.6 * Power(((3 * EntryHalfLength * AValue * AValue) + (EntrySpeed * EntrySpeed * EntrySpeed / 3.6 / 3.6 / 3.6)), 0.333334);
3658 
3659  double ExitSpeedAtMaxBraking;
3660  // below introduced at v2.4.0, was ExitSpeedFull = LimitingSpeed; but that allowed very high brake rates when
3661  // took signaller control of a fast failed train with signaller limiting speed 30km/h
3663  {
3664  ExitSpeedAtMaxBraking = 0;
3665  }
3666  else
3667  {
3668  ExitSpeedAtMaxBraking = sqrt((EntrySpeed * EntrySpeed) - 2 * MaxBrakeRate * FrontElementLength);
3669  }
3670  double SpeedToUse;
3671  // use the highest of LimitingSpeed or ExitSpeedAtMaxBraking - added at v2.4.2 because trains entering at a continuation with zero (or very low) speed
3672  // & 2 elements before signal caused ExitSpeedAtMaxBraking & hence DistanceAtHalfBraking and DistanceAtThreeQuarterBraking to be zero, so no restriction was recognised
3673  // for first element & train accelerated at maximum rate, then at 2nd element train couldn't brake in time and overran the signal - notified by Micke via Discord on 02/06/20
3674  if(ExitSpeedAtMaxBraking > LimitingSpeed)
3675  {
3676  SpeedToUse = ExitSpeedAtMaxBraking;
3677  }
3678  else
3679  {
3680  SpeedToUse = LimitingSpeed;
3681  }
3682  if(ExitSpeedFull > SpeedToUse)
3683  {
3684  ExitSpeedFull = SpeedToUse;
3685  }
3686  DistanceAtHalfBraking = ExitSpeedFull * ExitSpeedFull / 3.6 / 3.6 / MaxBrakeRate;
3687  DistanceAtThreeQuarterBraking = ExitSpeedFull * ExitSpeedFull / 3.6 / 3.6 / 1.5 / MaxBrakeRate; // used for signaller stops
3688 
3689  //now enter a do loop to examine each element in turn from the front of the train to calc the cumulative length and to see if a stop is required (flag set if so -
3690  //RedSignalFlag, BuffersFlag, StationFlag, TrainInFrontInSignallerModeFlag, SignallerStopRequired, StepForwardFlag) in which case there are no more loops
3691  // break out of the loop when ((CumulativeLength - FrontElementLength) < DistanceAtHalfBraking ) && ((!BuffersOrContinuationNowFlag && !ContinuationNextFlag) || SignallerStoppingFlag);
3692 
3693  do
3694  {
3695  RedSignalFlag = false;
3696  BuffersFlag = false;
3697  StationFlag = false;
3698  BuffersOrContinuationNowFlag = false;
3699  ContinuationNextFlag = false;
3700  // have to reset this after the above test
3701  // add current element length to CumulativeLength
3702  CumulativeLength += (2 * CurrentElementHalfLength);
3703  if((CumulativeLength >= DistanceAtThreeQuarterBraking) && (TrainMode == Signaller) && SignallerStoppingFlag)
3704  {
3705  SignallerStopRequired = true;
3706  // once set stays set until SignallerStoppingFlag reset, providing !BuffersOrContinuationNowFlag,
3707  // set SignallerStopBrakeRate to stop in CumulativeLength unless already higher (i.e. can only increase)
3708  double TempBR = EntrySpeed * EntrySpeed / 2 / 3.6 / 3.6 / CumulativeLength;
3709  if(SignallerStopBrakeRate < TempBR)
3710  {
3711  SignallerStopBrakeRate = TempBR;
3712  }
3713  }
3714  // first check for stops within the length of the current element, where don't want any more checks & don't want
3715  // to add in any extra to the CumulativeLength. Only applies for buffers & station stops as signals should have been caught
3716  // during the last loop when the NextTrackVectorPosition was the signal.
3717 
3718  // check if current element is a buffer
3719  if(Track->TrackElementAt(374, CurrentTrackVectorPosition).TrackType == Buffers)
3720  {
3721  // no need to add in the length of this element to CumulativeLength as already included
3722  BuffersFlag = true;
3723  }
3724  // check if current element is a station stop
3725  if(TrainMode == Timetable)
3726  {
3727  bool StopRequired = false;
3728  if(!TimetableFinished && (NameInTimetableBeforeCDT(12, Track->TrackElementAt(375, CurrentTrackVectorPosition).ActiveTrackElementName,
3729  StopRequired) > -1) && ((Track->TrackElementAt(376, CurrentTrackVectorPosition).StationEntryStopLinkPos1 == EntryPos) ||
3730  (Track->TrackElementAt(377, CurrentTrackVectorPosition).StationEntryStopLinkPos2 == EntryPos)))
3731  {
3732  // no need to add in the length of element to CumulativeLength
3733  if(StopRequired)
3734  {
3735  StationFlag = true;
3736  }
3737  }
3738  }
3739  else
3740  {
3741  StationFlag = false;
3742  }
3743  // set NextHalfLength & NextSpeedLimit, but only if current element not buffers or exit continuation - no next element for them
3744  if(((Track->TrackElementAt(354, CurrentTrackVectorPosition).TrackType == Buffers) || (Track->TrackElementAt(355,
3745  CurrentTrackVectorPosition).TrackType == Continuation)) && (EntryPos == 1))
3746  {
3747  BuffersOrContinuationNowFlag = true;
3748  }
3749  if(!BuffersOrContinuationNowFlag && !BuffersFlag && !StationFlag) // skip if buffers or station flags already set
3750  {
3751  if(Track->TrackElementAt(356, CurrentTrackVectorPosition).TrackType == Points)
3752  {
3753  if((EntryPos == 0) || (EntryPos == 2))
3754  {
3755  if(Track->TrackElementAt(357, CurrentTrackVectorPosition).Attribute == 0)
3756  {
3757  ExitPos = 1;
3758  }
3759  else
3760  {
3761  ExitPos = 3;
3762  }
3763  }
3764  else
3765  {
3766  ExitPos = 0;
3767  }
3768  }
3769  else
3770  {
3771  ExitPos = Track->GetNonPointsOppositeLinkPos(EntryPos);
3772  }
3773  NextTrackVectorPosition = Track->TrackElementAt(358, CurrentTrackVectorPosition).Conn[ExitPos];
3774  NextEntryPos = Track->TrackElementAt(359, CurrentTrackVectorPosition).ConnLinkPos[ExitPos];
3775  if(NextTrackVectorPosition > -1)
3776  {
3777  if(Track->TrackElementAt(845, NextTrackVectorPosition).TrackType == Points)
3778  // this test & section added at v0.6
3779  {
3780  if((NextEntryPos == 0) || (NextEntryPos == 2))
3781  {
3782  if(Track->TrackElementAt(846, NextTrackVectorPosition).Attribute == 0)
3783  {
3784  NextElementHalfLength = (Track->TrackElementAt(847, NextTrackVectorPosition).Length01) / 2;
3785  NextSpeedLimit = Track->TrackElementAt(848, NextTrackVectorPosition).SpeedLimit01;
3786  }
3787  else
3788  {
3789  NextElementHalfLength = (Track->TrackElementAt(849, NextTrackVectorPosition).Length23) / 2;
3790  NextSpeedLimit = Track->TrackElementAt(850, NextTrackVectorPosition).SpeedLimit23;
3791  }
3792  }
3793  else if(NextEntryPos == 1)
3794  {
3795  NextElementHalfLength = (Track->TrackElementAt(851, NextTrackVectorPosition).Length01) / 2;
3796  NextSpeedLimit = Track->TrackElementAt(852, NextTrackVectorPosition).SpeedLimit01;
3797  }
3798  else // == 3
3799  {
3800  NextElementHalfLength = (Track->TrackElementAt(853, NextTrackVectorPosition).Length23) / 2;
3801  NextSpeedLimit = Track->TrackElementAt(854, NextTrackVectorPosition).SpeedLimit23;
3802  }
3803  }
3804  else
3805  {
3806  if(NextEntryPos > 1)
3807  {
3808  NextElementHalfLength = (Track->TrackElementAt(360, NextTrackVectorPosition).Length23) / 2;
3809  NextSpeedLimit = Track->TrackElementAt(361, NextTrackVectorPosition).SpeedLimit23;
3810  }
3811  else
3812  {
3813  NextElementHalfLength = (Track->TrackElementAt(362, NextTrackVectorPosition).Length01) / 2;
3814  NextSpeedLimit = Track->TrackElementAt(363, NextTrackVectorPosition).SpeedLimit01;
3815  }
3816  }
3817  }
3818  else
3819  {
3820  throw Exception("Error - Trying to access NextTrackVectorPosition when none present in SetTrainMovementValues");
3821  }
3822  // now check for stops, first cover those where don't want to add in length of next element
3823  // check if next element is a red signal - Attr 0,
3824  // note that this doesn't apply to trains stopped at a red signal since the signal position is
3825  // CurrentTrackVectorPosition not NextTrackVectorPosition
3826  bool StopRequired;
3827  if(Track->TrackElementAt(364, NextTrackVectorPosition).Config[Track->GetNonPointsOppositeLinkPos(NextEntryPos)] == Signal)
3828  {
3829  if(Track->TrackElementAt(365, NextTrackVectorPosition).Attribute == 0)
3830  {
3831  // no need to add in the length of element to CumulativeLength
3832  RedSignalFlag = true;
3833  }
3834  // next element is a red signal
3835  }
3836  // check if current element is a station & next element contains a train - trains will always stop without crashing at a
3837  // station they are due to stop at even if there is a train in front blocking the normal stop position - providing there is
3838  // at least one platform element free
3840  CurrentTrackVectorPosition).ActiveTrackElementName, StopRequired) > -1) && Track->OtherTrainOnTrack(3, NextTrackVectorPosition,
3841  NextEntryPos, TrainID))
3842  {
3843  // no need to add in the length of element to CumulativeLength
3844  if(StopRequired)
3845  {
3846  StationFlag = true;
3847  }
3848  }
3849  // check if next element contains a train & in Signaller mode (always stops for train in front if in signaller mode)
3850  else if((TrainMode == Signaller) && Track->OtherTrainOnTrack(4, NextTrackVectorPosition, NextEntryPos, TrainID))
3851  // (Track->TrackElementAt(651, NextTrackVectorPosition).TrainIDOnElement > -1))
3852  {
3853  // no need to add in the length of element to CumulativeLength
3854  TrainInFrontInSignallerModeFlag = true;
3855  }
3856  // check if next element is a buffer
3857  else if(Track->TrackElementAt(366, NextTrackVectorPosition).TrackType == Buffers)
3858  {
3859  // need to add in the length of that element to CumulativeLength
3860  CumulativeLength += Track->TrackElementAt(367, NextTrackVectorPosition).Length01;
3861  BuffersFlag = true;
3862  }
3863  // check if next element is a station stop
3865  NextTrackVectorPosition).ActiveTrackElementName, StopRequired) > -1) && ((Track->TrackElementAt(371,
3866  NextTrackVectorPosition).StationEntryStopLinkPos1 == EntryPos) || (Track->TrackElementAt(372,
3867  NextTrackVectorPosition).StationEntryStopLinkPos2 == EntryPos)))
3868  {
3869  // need to add in the length of that element to CumulativeLength if a stop required
3870  if(StopRequired)
3871  {
3872  StationFlag = true;
3873  CumulativeLength += Track->TrackElementAt(373, NextTrackVectorPosition).Length01;
3874  }
3875  }
3876  }
3877  //now can decide whether need to stop over CumulativeLength
3878  if(RedSignalFlag || BuffersFlag || StationFlag || TrainInFrontInSignallerModeFlag || SignallerStopRequired || StepForwardFlag) // no more loops
3879  {
3880  // have to come to a stop over CumulativeLength
3881  if(CumulativeLength == FrontElementLength)
3882  // will be if StepForwardFlag (if stopped to begin with on zero power then earlier check will intercept it and it won't reach here
3883  // only one length to go before stop so check whether need to accelerate for half length then brake for latter
3884  // half; calc speed at halfway point that corresponds to half braking rate for latter half of track element,
3885  // and if less than EntrySpeed then skip this section (don't need any acceleration)
3886  // if not calc speed at halfway point & if less than above set half speed to this value;
3887  // use constant acceleration in calculating half time point
3888  {
3889  MaxExitSpeed = 0;
3890  double MaxHalfSpeed;
3891  double MaxHalfSpeedAtHalfBraking = 3.6 * sqrt(MaxBrakeRate * FrontElementLength / 2);
3892  // have to halve the element length, & can't be zero or negative so no need to test
3893  // if(MaxHalfSpeedAtHalfBraking > LimitingSpeed) MaxHalfSpeed = LimitingSpeed; else MaxHalfSpeed = MaxHalfSpeedAtHalfBraking;
3894  if(MaxHalfSpeedAtHalfBraking > FrontElementMaxSpeed)
3895  {
3896  MaxHalfSpeed = FrontElementMaxSpeed;
3897  }
3898  else
3899  {
3900  MaxHalfSpeed = MaxHalfSpeedAtHalfBraking;
3901  }
3902  if(MaxHalfSpeed > (2 * EntrySpeed))
3903  // use 2x to prevent kangarooing at last element when had
3904  // been braking smoothly at less that 50% braking rate, 2x should prevent all but extreme cases
3905  {
3906  ExitSpeedHalf = 3.6 * Power(((1.5 * EntryHalfLength * AValue * AValue) + (EntrySpeed * EntrySpeed * EntrySpeed / 3.6 / 3.6 / 3.6)),
3907  0.333334);
3908  bool HalfSpeedLimited = false;
3909  if(MaxHalfSpeed < ExitSpeedHalf)
3910  {
3911  ExitSpeedHalf = MaxHalfSpeed;
3912  HalfSpeedLimited = true;
3913  }
3914  if(PowerAtRail > 1)
3915  // added at v2.4.0 in case reach here with failed train, when can't use AValue in denominator as close zero
3916  {
3917  // [km/h/3.6 = m/s]
3918  ExitTimeHalf =
3919  EntryTime + TDateTime(((ExitSpeedHalf * ExitSpeedHalf) - (EntrySpeed * EntrySpeed)) / 3.6 / 3.6 / (AValue * AValue) / 86400.0);
3920  }
3921  else
3922  {
3923  ExitTimeHalf = EntryTime + TDateTime(EntryHalfLength * 3.6 / EntrySpeed / 86400.0);
3924  }
3925  // the above is the time taken to accelerate to ExitSpeedHalf, so if this is reached before the half
3926  // way point (i.e. HalfSpeedLimited is set) then the time will be too short; change later to equal the
3927  // braking time; not fully accurate but better to be equal than have a short acceleration period followed
3928  // by a long braking period
3929  ExitSpeedFull = 0;
3930  TempBrakeRate = ExitSpeedHalf * ExitSpeedHalf / 2 / 3.6 / 3.6 / EntryHalfLength;
3931  if(TempBrakeRate > MaxBrakeRate)
3932  {
3933  TempBrakeRate = MaxBrakeRate;
3934  }
3935  // shouldn't be but leave in anyway
3936  if(TempBrakeRate > BrakeRate)
3937  {
3938  BrakeRate = TempBrakeRate;
3939  }
3940  // BrakeRate may already have been set in an earlier loop so don't want to reduce it
3941  ExitTimeFull = ExitTimeHalf + TDateTime(ExitSpeedHalf / 3.6 / BrakeRate / 86400.0);
3942  if(HalfSpeedLimited)
3943  // this is the change referred to above
3944  {
3945  TDateTime BrakingTime = ExitTimeFull - ExitTimeHalf;
3946  ExitTimeHalf = EntryTime + BrakingTime;
3947  ExitTimeFull = ExitTimeHalf + BrakingTime;
3948  }
3949  OneLengthAccelDecel = true; //used in TrackTrainFloat in InterfaceUnit.cpp to show accelerating for first half move then decelerating
3950  Utilities->CallLogPop(1095);
3951  return;
3952  }
3953  }
3954  // set braking to achieve speed = 0 @ CumulativeLength up to MaxBrakeRate
3955  // calc MaxExitSpeed for element at EntryPosition & set to this or existing val if lower,
3956  // calc th, tf, sh, & sf
3957  TempBrakeRate = EntrySpeed * EntrySpeed / 2 / 3.6 / 3.6 / CumulativeLength;
3958  if(TempBrakeRate > MaxBrakeRate)
3959  {
3960  TempBrakeRate = MaxBrakeRate;
3961  }
3962  if(TempBrakeRate > BrakeRate)
3963  {
3964  BrakeRate = TempBrakeRate;
3965  }
3966  // BrakeRate may already have been set in an earlier loop so don't want to reduce it
3967  if(SignallerStopRequired)
3968  // set BrakeRate to max of its calculated value or SignallerStopBrakeRate
3969  {
3971  {
3973  // this prevents the brakerate from reducing for a signaller stop
3974  // regardless of other conditions that may change as progress round the loop
3975  }
3976  }
3978  // prevents BrakeRate dropping below SignallerStopBrakeRate once it's been set whether or not SignallerStopRequired set
3979  // SignallerStopRequired may not be set if a red signal found in a later calc, & brakerate may then drop
3980  {
3982  }
3983  int TempMaxExitSpeed;
3984  // calc current value & if less than MaxExitSpeed set that to this
3985  MaxExitSpeedAtHalfBrakingSquared = 3.6 * 3.6 * MaxBrakeRate * (CumulativeLength - FrontElementLength);
3986  if(MaxExitSpeedAtHalfBrakingSquared < 10)
3987  {
3988  MaxExitSpeedAtHalfBraking = 0;
3989  }
3990  else
3991  {
3992  MaxExitSpeedAtHalfBraking = sqrt(MaxExitSpeedAtHalfBrakingSquared);
3993  }
3994  // if(MaxExitSpeedAtHalfBraking > LimitingSpeed) MaxExitSpeed = LimitingSpeed; else MaxExitSpeed = MaxExitSpeedAtHalfBraking;
3995  // I think the above was dropped because it could cause MaxExitSpeed to increase (MaxExitSpeed is an external variable retained between loops)
3996  if(MaxExitSpeedAtHalfBraking > FrontElementMaxSpeed)
3997  {
3998  TempMaxExitSpeed = FrontElementMaxSpeed;
3999  }
4000  else
4001  {
4002  TempMaxExitSpeed = MaxExitSpeedAtHalfBraking;
4003  }
4004  if(TempMaxExitSpeed < MaxExitSpeed)
4005  {
4006  MaxExitSpeed = TempMaxExitSpeed;
4007  }
4008  // here have EntrySpeed & MaxExitSpeed (for the next element), BrakeRate (to bring speed to zero over
4009  // Cumulativelength, and Cumulativelength
4010 
4011  if((EntrySpeed > MaxExitSpeed) || SignallerStopRequired || (SignallerStopBrakeRate > 0.01)) // need to brake
4012  {
4013  ExitSpeedHalfSquared = (EntrySpeed * EntrySpeed) - (3.6 * 3.6 * 2 * BrakeRate * EntryHalfLength);
4014  if(ExitSpeedHalfSquared < 10)
4015  {
4016  ExitSpeedHalf = 0;
4017  }
4018  else
4019  {
4020  ExitSpeedHalf = sqrt(ExitSpeedHalfSquared);
4021  }
4022  ExitTimeHalf = EntryTime + TDateTime((EntrySpeed - ExitSpeedHalf) / 3.6 / BrakeRate / 86400.0);
4023  ExitSpeedFullSquared = (EntrySpeed * EntrySpeed) - (3.6 * 3.6 * 4 * BrakeRate * EntryHalfLength);
4024  if(ExitSpeedFullSquared < 10)
4025  {
4026  ExitSpeedFull = 0;
4027  }
4028  else
4029  {
4030  ExitSpeedFull = sqrt(ExitSpeedFullSquared);
4031  }
4032  if((StationFlag) && (CumulativeLength == FrontElementLength))
4033  {
4034  ExitSpeedFull = 0;
4035  // force a stop for station (not for buffers or red signal)
4036  }
4037  ExitTimeFull = EntryTime + TDateTime((EntrySpeed - ExitSpeedFull) / 3.6 / BrakeRate / 86400.0);
4038  }
4039  // new condition at v2.4.0
4040  else if(PowerAtRail <= 1)
4041  // use EntrySpeed, CumulativeLength & BrakeRate to calculate the half and full exit times and speeds for next element
4042  // avoid using AValue in denominator or have excessively long times
4043  {
4044  ExitSpeedHalfSquared = (EntrySpeed * EntrySpeed) - (3.6 * 3.6 * BrakeRate * FrontElementLength);
4045  ExitSpeedHalf = sqrt(ExitSpeedHalfSquared);
4046  ExitTimeHalf = EntryTime + TDateTime((EntrySpeed - ExitSpeedHalf) / (3.6 * BrakeRate * 86400.0));
4047 
4048  ExitSpeedFullSquared = (EntrySpeed * EntrySpeed) - (3.6 * 3.6 * 2 * BrakeRate * FrontElementLength);
4049  ExitSpeedFull = sqrt(ExitSpeedFullSquared);
4050  ExitTimeFull = EntryTime + TDateTime((EntrySpeed - ExitSpeedFull) / (3.6 * BrakeRate * 86400.0));
4051  }
4052  else // e.g. moving towards a signal or station after a speed limit, so can accelerate unless no power
4053  // without the power need above condition or have hours of delay times, above added at v2.4.0
4054  {
4055  // for accelerating the energy input rate (PowerAtRail) is constant so the following formuli are used:-
4056  // (1) V^2/(3.6^2) - U^2/(3.6^2) = A^2T; (2) V = 3.6 * ((1.5*S*A^2) + U^3/(3.6^3))^0.333334;
4057  // where A is a constant (2*PowerAtRail/Mass)^0.5; V = final speed in kph, U = initial speed in kph, S = distance & T = time, note that km/h/3.6 = m/s
4058  // This is a bit unrealistic during the early acceleration phase as it will be too rapid, but shouldn't affect the running unduly
4059  BrakeRate = 0;
4060  ExitSpeedHalf = 3.6 * Power(((1.5 * EntryHalfLength * AValue * AValue) + (EntrySpeed * EntrySpeed * EntrySpeed / 3.6 / 3.6 / 3.6)),
4061  0.333334);
4062  ExitSpeedFull = 3.6 * Power(((3 * EntryHalfLength * AValue * AValue) + (EntrySpeed * EntrySpeed * EntrySpeed / 3.6 / 3.6 / 3.6)), 0.333334);
4063  // above valid for ExitSpeedHalf & Full <= MaxExitSpeed
4065  // can accelerate continually over the half length
4066  {
4067  ExitTimeHalf = EntryTime + TDateTime(((ExitSpeedHalf * ExitSpeedHalf) - (EntrySpeed * EntrySpeed)) / 3.6 / 3.6 / (AValue * AValue)
4068  / 86400.0);
4070  // can accelerate continually over the full length
4071  // i.e both (ExitSpeedHalf <= MaxExitSpeed) & (ExitSpeedFull <= MaxExitSpeed)
4072  {
4073  ExitTimeFull =
4074  EntryTime + TDateTime(((ExitSpeedFull * ExitSpeedFull) - (EntrySpeed * EntrySpeed)) / 3.6 / 3.6 / (AValue * AValue) / 86400.0);
4075  }
4076  else // (ExitSpeedHalf <= MaxExitSpeed) but (ExitSpeedFull > MaxExitSpeed)
4077  // accelerate to MaxExitSpeed then reamin at this speed for rest of element
4078  {
4079  // added at v0.6 as a safeguard
4080  if(MaxExitSpeed < EntrySpeed)
4081  {
4083  }
4084  // to prevent DeltaExitTimeToMaxInSecs being negative
4085  if(MaxExitSpeed < 1)
4086  {
4087  MaxExitSpeed = 1;
4088  }
4089  // to prevent divide by zero error
4090  // below as was
4092  double DeltaExitTimeToMaxInSecs = ((MaxExitSpeed * MaxExitSpeed) - (EntrySpeed * EntrySpeed)) / 3.6 / 3.6 / (AValue * AValue);
4093  double DistanceToMax = ((MaxExitSpeed * MaxExitSpeed * MaxExitSpeed) - (EntrySpeed * EntrySpeed * EntrySpeed)) / 3.6 / 3.6 / 3.6 /
4094  (1.5 * AValue * AValue);
4095  double RemainingDistance = double(FrontElementLength) - DistanceToMax;
4096  double DeltaRemainingTimeInSecs = 3.6 * RemainingDistance / MaxExitSpeed;
4097  ExitTimeFull = EntryTime + TDateTime((DeltaExitTimeToMaxInSecs + DeltaRemainingTimeInSecs) / 86400.0);
4098  }
4099  }
4100  else // ExitSpeedHalf > MaxExitSpeed, so ExitSpeedFull must also be > MaxExitSpeed
4101  // accelerate over first half to MaxExitSpeed then remain at this speed for rest of the first and
4102  // second halves of the element
4103  {
4104  // added at v0.6 as a safeguard
4105  if(MaxExitSpeed < EntrySpeed)
4106  {
4108  }
4109  // to prevent DeltaExitTimeToMaxInSecs being negative
4110  if(MaxExitSpeed < 1)
4111  {
4112  MaxExitSpeed = 1; // to prevent divide by zero error
4113  }
4114  // below as was
4116  double DeltaExitTimeToMaxInSecs = ((MaxExitSpeed * MaxExitSpeed) - (EntrySpeed * EntrySpeed)) / 3.6 / 3.6 / (AValue * AValue);
4117  double DistanceToMax = ((MaxExitSpeed * MaxExitSpeed * MaxExitSpeed) - (EntrySpeed * EntrySpeed * EntrySpeed)) / 3.6 / 3.6 / 3.6 /
4118  (1.5 * AValue * AValue);
4119  double RemainingDistance = double(FrontElementLength / 2) - DistanceToMax;
4120  // remaining distance to half length
4121  double DeltaRemainingTimeInSecs = 3.6 * RemainingDistance / MaxExitSpeed;
4122  ExitTimeHalf = EntryTime + TDateTime((DeltaExitTimeToMaxInSecs + DeltaRemainingTimeInSecs) / 86400.0);
4124  ExitTimeFull = ExitTimeHalf + TDateTime(3.6 * EntryHalfLength / MaxExitSpeed / 86400.0);
4125  }
4126  }
4127  Utilities->CallLogPop(706);
4128  return;
4129  }
4130  else
4131  {
4132  if(!BuffersOrContinuationNowFlag)
4133  {
4134  if(NextSpeedLimit < LimitingSpeed)
4135  {
4136  LimitingSpeed = NextSpeedLimit;
4137  }
4138  }
4139  // calc max exit speed at half braking to ensure don't accelerate past it (if acceleration is required)
4140  int TempMaxExitSpeed;
4141  // calc current value & if less than MaxExitSpeed set that to this
4142  // Note that LimitingSpeed is the max value at the end of CumulativeLength, so MaxExitSpeedAtHalfBrakingSquared will be larger
4143  MaxExitSpeedAtHalfBrakingSquared = (LimitingSpeed * LimitingSpeed) + (3.6 * 3.6 * MaxBrakeRate * (CumulativeLength - FrontElementLength));
4144  if(MaxExitSpeedAtHalfBrakingSquared < 10)
4145  {
4146  MaxExitSpeedAtHalfBraking = 0;
4147  }
4148  else
4149  {
4150  MaxExitSpeedAtHalfBraking = sqrt(MaxExitSpeedAtHalfBrakingSquared);
4151  }
4152  if(MaxExitSpeedAtHalfBraking > FrontElementMaxSpeed)
4153  {
4154  TempMaxExitSpeed = FrontElementMaxSpeed;
4155  }
4156  else
4157  {
4158  TempMaxExitSpeed = MaxExitSpeedAtHalfBraking;
4159  }
4160  if(TempMaxExitSpeed < MaxExitSpeed)
4161  {
4162  MaxExitSpeed = TempMaxExitSpeed;
4163  }
4164  // MaxExitSpeed is an external variable & this can reduce its value
4165  if(EntrySpeed > LimitingSpeed)
4166  // note that LimitingSpeed is more restrictive than MaxExitSpeed
4167  // calc TempBrakeRate & set BrakeRate to this or keep existing val if higher
4168  {
4169  TempBrakeRate = ((EntrySpeed * EntrySpeed) - (LimitingSpeed * LimitingSpeed)) / 3.6 / 3.6 / 2 / CumulativeLength;
4170  if(TempBrakeRate > MaxBrakeRate)
4171  {
4172  TempBrakeRate = MaxBrakeRate;
4173  }
4174  // shouldn't be for speedlimits since all known in advance, but include anyway
4175  if(TempBrakeRate > BrakeRate)
4176  {
4177  BrakeRate = TempBrakeRate;
4178  }
4179  // BrakeRate may already have been set in an earlier loop so don't want to reduce it
4180  }
4181  }
4182  if(!BuffersOrContinuationNowFlag)
4183  {
4184  CurrentTrackVectorPosition = NextTrackVectorPosition;
4185  EntryPos = NextEntryPos;
4186  CurrentElementHalfLength = NextElementHalfLength;
4187  if(Track->TrackElementAt(378, NextTrackVectorPosition).TrackType == Continuation)
4188  {
4189  ContinuationNextFlag = true;
4190  }
4191  }
4192  }
4193  while(((CumulativeLength - FrontElementLength) < DistanceAtHalfBraking) && ((!BuffersOrContinuationNowFlag && !ContinuationNextFlag) ||
4195  // check from the end of the front element, if include the front element and could brake during it, then will skip further loops
4196  // & miss a stop requirement just beyond the front element. happened in Richard Standing's railway where a new service introduced
4197  // on a 100m length, with 20m length after & then a red signal - train accelerated over the 100m then caused a SPAD as too short a
4198  // stopping distance after it.
4199 
4200  //(!BuffersOrContinuationNowFlag && !ContinuationNextFlag) true when no continuation on either the next element and next but one element.
4201  //There won't be a buffer on the next element or would have caught earlier, just using this flag for convenience.
4202 
4203  // If SignallerStoppingFlag then don't exit loop because of an imminent continuation, because continuation
4204  // not immediately in front (if it is then LeadElement will be the continuation & SignallerStoppingFlag will be reset in UpdateTrain()),
4205  // need to at least give a chance to stop on signaller command, if keep moving until continuation is immediately in front then will
4206  // exit loop & that is OK as don't want to stop so close to a continuation, if that happens it means that the command to stop was given
4207  // too late
4208 
4209  // set final braking or acc'n speed & time values
4210  if(BrakeRate > 0.01)
4211  {
4212  ExitSpeedHalfSquared = (EntrySpeed * EntrySpeed) - (3.6 * 3.6 * 2 * BrakeRate * EntryHalfLength);
4213  if(ExitSpeedHalfSquared < 10)
4214  {
4215  ExitSpeedHalf = 0;
4216  }
4217  else
4218  {
4219  ExitSpeedHalf = sqrt(ExitSpeedHalfSquared);
4220  }
4221  ExitTimeHalf = EntryTime + TDateTime((EntrySpeed - ExitSpeedHalf) / 3.6 / BrakeRate / 86400.0);
4222  ExitSpeedFullSquared = (EntrySpeed * EntrySpeed) - (3.6 * 3.6 * 4 * BrakeRate * EntryHalfLength);
4223  if(ExitSpeedFullSquared < 10)
4224  {
4225  ExitSpeedFull = 0;
4226  }
4227  else
4228  {
4229  ExitSpeedFull = sqrt(ExitSpeedFullSquared);
4230  }
4231  ExitTimeFull = EntryTime + TDateTime((EntrySpeed - ExitSpeedFull) / 3.6 / BrakeRate / 86400.0);
4232  }
4233  else
4234  {
4235  // for accelerating the energy input rate (PowerAtRail) is constant so the following formuli are used:-
4236  // (1) V^2/(3.6^2) - U^2/(3.6^2) = A^2T; (2) V = 3.6 * ((1.5*S*A^2) + U^3/ (3.6)^3)^0.333334;
4237  // where A is a constant (2*PowerAtRail/Mass)^0.5; V = final speed in kph, U = initial speed in kph , S = distance in m & T = time
4238  // This is a bit unrealistic during the early acceleration phase as it will be too rapid, but shouldn't affect the running unduly
4239 
4240  BrakeRate = 0;
4241  ExitSpeedHalf = 3.6 * Power(((1.5 * EntryHalfLength * AValue * AValue) + (EntrySpeed * EntrySpeed * EntrySpeed / 3.6 / 3.6 / 3.6)), 0.333334);
4242  ExitSpeedFull = 3.6 * Power(((3 * EntryHalfLength * AValue * AValue) + (EntrySpeed * EntrySpeed * EntrySpeed / 3.6 / 3.6 / 3.6)), 0.333334);
4243  // above valid for ExitSpeedHalf & Full <= MaxExitSpeed
4245  {
4246  if(PowerAtRail > 1)
4247  // added at v2.4.0 in case reach here with failed train, when can't use AValue in denominator as close zero
4248  {
4249  // [km/h/3.6 = m/s]
4250  ExitTimeHalf = EntryTime + TDateTime(((ExitSpeedHalf * ExitSpeedHalf) - (EntrySpeed * EntrySpeed)) / 3.6 / 3.6 / (AValue * AValue)
4251  / 86400.0);
4252  }
4253  else
4254  {
4255  ExitTimeHalf = EntryTime + TDateTime(EntryHalfLength * 3.6 / EntrySpeed / 86400.0);
4256  }
4258  // (ExitSpeedHalf <= MaxExitSpeed) & (ExitSpeedFull <= MaxExitSpeed)
4259  {
4260  if(PowerAtRail > 1)
4261  // added at v2.4.0 in case reach here with failed train, when can't use AValue in denominator as close zero
4262  {
4263  // [km/h/3.6 = m/s]
4264  ExitTimeFull = EntryTime + TDateTime(((ExitSpeedFull * ExitSpeedFull) - (EntrySpeed * EntrySpeed)) / 3.6 / 3.6 / (AValue * AValue)
4265  / 86400.0);
4266  }
4267  else
4268  {
4269  ExitTimeFull = EntryTime + TDateTime(2 * EntryHalfLength * 3.6 / EntrySpeed / 86400.0);
4270  }
4271  }
4272  else // (ExitSpeedHalf <= MaxExitSpeed) & (ExitSpeedFull > MaxExitSpeed)
4273  {
4274  // added at v0.6 as a safeguard
4275  if(MaxExitSpeed < EntrySpeed)
4276  {
4278  }
4279  // to prevent DeltaExitTimeToMaxInSecs being negative
4280  if(MaxExitSpeed < 1)
4281  {
4282  MaxExitSpeed = 1; // to prevent divide by zero error
4283  }
4284  // below as was
4286  double DeltaExitTimeToMaxInSecs;
4287  double DistanceToMax;
4288  if(PowerAtRail > 1) // added at v2.4.0
4289  {
4290  DeltaExitTimeToMaxInSecs = ((MaxExitSpeed * MaxExitSpeed) - (EntrySpeed * EntrySpeed)) / 3.6 / 3.6 / (AValue * AValue);
4291  DistanceToMax = ((MaxExitSpeed * MaxExitSpeed * MaxExitSpeed) - (EntrySpeed * EntrySpeed * EntrySpeed)) / 3.6 / 3.6 / 3.6 /
4292  (1.5 * AValue * AValue);
4293  }
4294  else // shouldn't ever get here given that ExitSpeedFull > ExitSpeedHalf
4295  {
4296  DeltaExitTimeToMaxInSecs = 2 * EntryHalfLength * 3.6 / EntrySpeed;
4297  // these not really accurate but will be good enough
4298  DistanceToMax = EntryHalfLength;
4299  }
4300  double RemainingDistance = double(FrontElementLength) - DistanceToMax;
4301  double DeltaRemainingTimeInSecs = 3.6 * RemainingDistance / MaxExitSpeed;
4302  ExitTimeFull = EntryTime + TDateTime((DeltaExitTimeToMaxInSecs + DeltaRemainingTimeInSecs) / 86400.0);
4303  }
4304  }
4305  else // ExitSpeedHalf > MaxExitSpeed, ExitSpeedFull must be > MaxExitSpeed
4306  {
4307  // added at v0.6 as a safeguard
4308  if(MaxExitSpeed < EntrySpeed)
4309  {
4311  }
4312  // to prevent DeltaExitTimeToMaxInSecs being negative
4313  if(MaxExitSpeed < 1)
4314  {
4315  MaxExitSpeed = 1; // to prevent divide by zero error
4316  }
4317  // below as was
4319  double DeltaExitTimeToMaxInSecs;
4320  double DistanceToMax;
4321  if(PowerAtRail > 1) // added at v2.4.0
4322  {
4323  DeltaExitTimeToMaxInSecs = ((MaxExitSpeed * MaxExitSpeed) - (EntrySpeed * EntrySpeed)) / 3.6 / 3.6 / (AValue * AValue);
4324  DistanceToMax = ((MaxExitSpeed * MaxExitSpeed * MaxExitSpeed) - (EntrySpeed * EntrySpeed * EntrySpeed)) / 3.6 / 3.6 / 3.6 /
4325  (1.5 * AValue * AValue);
4326  }
4327  else
4328  {
4329  DeltaExitTimeToMaxInSecs = 2 * EntryHalfLength * 3.6 / EntrySpeed;
4330  // these not really accurate but will be good enough
4331  DistanceToMax = EntryHalfLength / 2;
4332  }
4333  double RemainingDistance = double(FrontElementLength / 2) - DistanceToMax; // remaining distance to half length
4334  double DeltaRemainingTimeInSecs = 3.6 * RemainingDistance / MaxExitSpeed;
4335  ExitTimeHalf = EntryTime + TDateTime((DeltaExitTimeToMaxInSecs + DeltaRemainingTimeInSecs) / 86400.0);
4337  ExitTimeFull = ExitTimeHalf + TDateTime(3.6 * EntryHalfLength / MaxExitSpeed / 86400.0);
4338  }
4339  }
4340  }
4341 
4342  else // SPADFlag set
4343  {
4345  ExitSpeedHalfSquared = (EntrySpeed * EntrySpeed) - (3.6 * 3.6 * 2 * BrakeRate * EntryHalfLength);
4346  if(ExitSpeedHalfSquared < 10)
4347  {
4348  ExitSpeedHalf = 0;
4349  }
4350  else
4351  {
4352  ExitSpeedHalf = sqrt(ExitSpeedHalfSquared);
4353  }
4354  ExitTimeHalf = EntryTime + TDateTime((EntrySpeed - ExitSpeedHalf) / 3.6 / BrakeRate / 86400.0);
4355  ExitSpeedFullSquared = (EntrySpeed * EntrySpeed) - (3.6 * 3.6 * 4 * BrakeRate * EntryHalfLength);
4356  if(ExitSpeedFullSquared < 10)
4357  {
4358  ExitSpeedFull = 0;
4359  }
4360  else
4361  {
4362  ExitSpeedFull = sqrt(ExitSpeedFullSquared);
4363  }
4364  ExitTimeFull = EntryTime + TDateTime((EntrySpeed - ExitSpeedFull) / 3.6 / BrakeRate / 86400.0);
4365 
4366  // check if the exit speed is < 80% of the stopping speed for the next element, and if so stop at end of this element
4367  // this is because would stop short of end of next element (in reality the time to reach the end of the next element
4368  // would be too short (could be so short as to make the train jump) as time is calculated purely on speed & brake rate);
4369  // 80% is used as the brake rate might be set to come to a halt at the end of the next element in which case the speed
4370  // will be the stopping speed.
4371  if(ExitSpeedFull > 0)
4372  {
4373  if(Track->TrackElementAt(746, CurrentTrackVectorPosition).TrackType == Points)
4374  {
4375  if((EntryPos == 0) || (EntryPos == 2))
4376  {
4377  if(Track->TrackElementAt(747, CurrentTrackVectorPosition).Attribute == 0)
4378  {
4379  ExitPos = 1;
4380  }
4381  else
4382  {
4383  ExitPos = 3;
4384  }
4385  }
4386  else
4387  {
4388  ExitPos = 0;
4389  }
4390  }
4391  else
4392  {
4393  ExitPos = Track->GetNonPointsOppositeLinkPos(EntryPos);
4394  }
4395  NextTrackVectorPosition = Track->TrackElementAt(748, CurrentTrackVectorPosition).Conn[ExitPos];
4396  NextEntryPos = Track->TrackElementAt(749, CurrentTrackVectorPosition).ConnLinkPos[ExitPos];
4397  if(NextTrackVectorPosition > -1) // not a continuation or buffer
4398  {
4399  int NextElementLength;
4400  if(NextEntryPos > 1)
4401  {
4402  NextElementLength = (Track->TrackElementAt(750, NextTrackVectorPosition).Length23);
4403  }
4404  else
4405  {
4406  NextElementLength = (Track->TrackElementAt(751, NextTrackVectorPosition).Length01);
4407  }
4408  double NextStoppingSpeed = sqrt(3.6 * 3.6 * 2 * BrakeRate * NextElementLength);
4409  if(ExitSpeedFull < (0.8 * NextStoppingSpeed))
4410  {
4411  ExitSpeedFull = 0;
4412  }
4413  }
4414  }
4415  }
4416  // allow all values to be set normally in case need to brake, then test for zero power & need to coast
4417  if(PowerAtRail < 1) // new at v2.4.0 note that km/h/3.6 = m/s
4418  {
4419  // bring to a stop in 20 elements at 100km/h & assume each 100m long for calculating exit times but if on a continuation maintain speed
4420  if(LeadElement > -1)
4421  {
4423  // don't stop on a continuation either entering or leaving
4424  {
4427  MaxExitSpeed = LimitingSpeed;
4428  BrakeRate = 0;
4429  ExitTimeHalf = EntryTime + TDateTime((50 * 3.6 / (EntrySpeed) / 86400.0));
4430  // assume length is 50m for ease of calc
4431  ExitTimeFull = ExitTimeHalf + TDateTime((50 * 3.6 / (ExitSpeedHalf) / 86400.0));
4432  Utilities->CallLogPop(2126);
4433  return;
4434  }
4435  }
4436  else if(MidElement > -1)
4437  {
4439  {
4442  MaxExitSpeed = LimitingSpeed;
4443  BrakeRate = 0;
4444  ExitTimeHalf = EntryTime + TDateTime((50 * 3.6 / (EntrySpeed) / 86400.0));
4445  // assume length is 50m for ease of calc
4446  ExitTimeFull = ExitTimeHalf + TDateTime((50 * 3.6 / (ExitSpeedHalf) / 86400.0));
4447  Utilities->CallLogPop(2127);
4448  return;
4449  }
4450  }
4451  else if(LagElement > -1)
4452  {
4454  {
4457  MaxExitSpeed = LimitingSpeed;
4458  BrakeRate = 0;
4459  ExitTimeHalf = EntryTime + TDateTime((50 * 3.6 / (EntrySpeed) / 86400.0));
4460  // assume length is 50m for ease of calc
4461  ExitTimeFull = ExitTimeHalf + TDateTime((50 * 3.6 / (ExitSpeedHalf) / 86400.0));
4462  Utilities->CallLogPop(2128);
4463  return;
4464  }
4465  }
4466  if(EntrySpeed > 7.5) // keep going for at least another element
4467  {
4468  ExitSpeedHalf = EntrySpeed - 2.5;
4469  ExitSpeedFull = EntrySpeed - 5;
4470  MaxExitSpeed = LimitingSpeed;
4471  BrakeRate = 0;
4472  ExitTimeHalf = EntryTime + TDateTime((50 * 3.6 / (EntrySpeed - 1.25) / 86400.0));
4473  // assume length is 50m for ease of calc
4474  ExitTimeFull = ExitTimeHalf + TDateTime((50 * 3.6 / (ExitSpeedHalf - 1.25) / 86400.0));
4475  Utilities->CallLogPop(2129);
4476  return;
4477  }
4478  else // stop immediately, don't enter next element, floating label had displayed last exit speed full on 2nd half move so with zero when fully on element
4479  {
4480  // will appear to have slowed at steady rate
4481  ExitSpeedHalf = 0;
4482  ExitSpeedFull = 0;
4483  MaxExitSpeed = LimitingSpeed;
4484  BrakeRate = 0;
4485  ExitTimeHalf = EntryTime + TDateTime(1/24); //set this high in case used later though unlikely
4486  ExitTimeFull = EntryTime + TDateTime(1/23); //set about 2.5 mins later than half time
4487  StoppedWithoutPower = true;
4488  Utilities->CallLogPop(2130);
4489  return;
4490  }
4491  }
4492  // TempBrakeRate=MinSingle; TempBrakeRate=MaxSingle; TempBrakeRate=MinDouble; TempBrakeRate=MaxDouble;//included to stop warnings from unused declarations in math.hpp
4493  // TempBrakeRate=MinExtended; TempBrakeRate=MaxExtended; TempBrakeRate=MinComp; TempBrakeRate=MaxComp;//included to stop warnings from unused declarations in math.hpp
4494  Utilities->CallLogPop(707);
4495 }
4496 // ---------------------------------------------------------------------------
4497 /*
4498  bool TTrain::IsTerminalStation(int TrackVectorPosition, int EntryPos)
4499  {
4500  int NextExitPos;
4501  TTrackElement NextElement = Track->TrackElementAt(379, TrackVectorPosition), TempElement;
4502  if(TimetableVector.empty()) return false;
4503  if(NextElement.ActiveTrackElementName != TimetableVector.begin()->LocationName) return false;
4504  while((NextElement.ActiveTrackElementName == TimetableVector.begin()->LocationName) && (NextElement.TrackType != Buffers))
4505  {
4506  //check for points & follow attribute, but don't worry about a derail as that dealt with elsewhere
4507  if((NextElement.TrackType != Points) || ((EntryPos != 0) && (EntryPos != 2)))
4508  {
4509  NextExitPos = Track->GetNonPointsOppositeLinkPos(EntryPos);
4510  }
4511  else if((NextElement.TrackType == Points) && ((EntryPos == 0) || (EntryPos == 2)))
4512  {
4513  if(NextElement.Attribute == 0) NextExitPos = 1; else NextExitPos = 3;
4514  }
4515  TempElement = Track->TrackElementAt(380, NextElement.Conn[NextExitPos]);//need temp as NextElement used in next step
4516  NextElement = TempElement;
4517  }
4518  if(NextElement.ActiveTrackElementName != TimetableVector.begin()->LocationName) return false;
4519  if(NextElement.TrackType == Buffers) return true;
4520  return false;//shouldn't reach here but include to prevent compiler return warning
4521  }
4522 */
4523 // ---------------------------------------------------------------------------
4524 
4525 int TTrain::NameInTimetableBeforeCDT(int Caller, AnsiString Name, bool &Stop)
4526 /*
4527  returns the number by which the train ActionVectorEntryPtr needs
4528  to be incremented to point to the location arrival entry or passtime entry before a change of direction. Used to display missed
4529  actions when a stop or pass location has been reached before other timetabled actions have been carried out. If can't find it, or Name
4530  is "", -1 is returned. A change of direction is the limit of the search because a train may not stop at a location on the way out
4531  but stop on way back, and in these circumstances no actions have been missed. Stop indicates whether the train will stop at (true)
4532  or pass (false) the location.
4533 */{
4534  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",NameInTimetableBeforeCDT," + Name + "," + HeadCode);
4535  Stop = false;
4536  if(TimetableFinished || (Name == ""))
4537  {
4538  Utilities->CallLogPop(957);
4539  return(-1);
4540  }
4541  // start looking from current pointer position
4542  for(TActionVectorEntry * Ptr = ActionVectorEntryPtr; Ptr < &TrainDataEntryPtr->ActionVector.back(); Ptr++)
4543  {
4544  if((Ptr->Command == "cdt") || (Ptr->FormatType == Repeat))
4545  {
4546  break;
4547  }
4548  if((Ptr->ArrivalTime > TDateTime(-1)) && (Ptr->LocationName == Name))
4549  {
4550  if((Ptr->FormatType == TimeLoc) || (Ptr->FormatType == TimeTimeLoc))
4551  {
4552  Stop = true;
4553  Utilities->CallLogPop(960);
4554  return (Ptr - ActionVectorEntryPtr);
4555  }
4556  }
4557  if((Ptr->EventTime > TDateTime(-1)) && (Ptr->LocationName == Name) && (Ptr->Command == "pas"))
4558  {
4559  Utilities->CallLogPop(1517);
4560  return (Ptr - ActionVectorEntryPtr);
4561  }
4562  }
4563  Utilities->CallLogPop(959);
4564  return(-1); // not found a valid entry
4565 }
4566 
4567 // ---------------------------------------------------------------------------
4568 
4570 /* Checks forward from train LeadElement, following leading point attributes but ignoring trailing point attributes,
4571  until finds either a train or a signal/buffers/continuation/loop. If finds a train returns false, else returns true.
4572  Ignores the call-on signal.
4573 */{
4574  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",ClearToNextSignal" + "," + HeadCode);
4575  int ReturnVal = 0;
4576 
4577  for(unsigned int x = 0; x < Track->TrackVector.size(); x++)
4578  {
4579  Track->TrackElementAt(1031, x).TempTrackMarker01 = false;
4580  Track->TrackElementAt(1032, x).TempTrackMarker23 = false;
4581  }
4582  int CurrentTrackVectorPosition = LeadElement, NextTrackVectorPosition;
4583  int EntryPos = LeadEntryPos, ExitPos, NextEntryPos;
4584 
4585  while(true)
4586  {
4587  if((Track->TrackElementAt(382, CurrentTrackVectorPosition).TrainIDOnElement > -1) && (Track->TrackElementAt(383,
4588  CurrentTrackVectorPosition).TrainIDOnElement != TrainID))
4589  {
4590  ReturnVal = 1;
4591  break;
4592  }
4593  if(((Track->TrackElementAt(384, CurrentTrackVectorPosition).TrackType == Buffers) || (Track->TrackElementAt(385,
4594  CurrentTrackVectorPosition).TrackType == Continuation)) && (EntryPos == 1))
4595  {
4596  ReturnVal = 2;
4597  break;
4598  }
4599  if((EntryPos < 2) && (Track->TrackElementAt(386, CurrentTrackVectorPosition).Config[1 - EntryPos] == Signal) && (Track->TrackElementAt(529,
4600  CurrentTrackVectorPosition).Attribute != 4)) // Attr 4 == call-on signal
4601  {
4602  ReturnVal = 3;
4603  break;
4604  }
4605  if((Track->TrackElementAt(387, CurrentTrackVectorPosition).TrackType == Bridge) || (Track->TrackElementAt(388,
4606  CurrentTrackVectorPosition).TrackType == Crossover))
4607  {
4608  if((EntryPos < 2) && (Track->TrackElementAt(523, CurrentTrackVectorPosition).TempTrackMarker01))
4609  // must be a loop - reached same point as examined earlier
4610  {
4611  ReturnVal = 4;
4612  break;
4613  }
4614  else if((EntryPos > 1) && (Track->TrackElementAt(524, CurrentTrackVectorPosition).TempTrackMarker23))
4615  {
4616  ReturnVal = 4;
4617  break;
4618  }
4619  }
4620  else
4621  {
4622  if((Track->TrackElementAt(525, CurrentTrackVectorPosition).TempTrackMarker01) || (Track->TrackElementAt(526,
4623  CurrentTrackVectorPosition).TempTrackMarker23))
4624  {
4625  ReturnVal = 4;
4626  break;
4627  }
4628  }
4629  if(EntryPos < 2)
4630  {
4631  Track->TrackElementAt(389, CurrentTrackVectorPosition).TempTrackMarker01 = true;
4632  }
4633  else
4634  {
4635  Track->TrackElementAt(527, CurrentTrackVectorPosition).TempTrackMarker23 = true;
4636  }
4637  if(Track->TrackElementAt(390, CurrentTrackVectorPosition).TrackType == Points)
4638  {
4639  if((EntryPos == 0) || (EntryPos == 2))
4640  {
4641  if(Track->TrackElementAt(391, CurrentTrackVectorPosition).Attribute == 0)
4642  {
4643  ExitPos = 1;
4644  }
4645  else
4646  {
4647  ExitPos = 3;
4648  }
4649  }
4650  else
4651  {
4652  ExitPos = 0;
4653  }
4654  }
4655  else
4656  {
4657  ExitPos = Track->GetNonPointsOppositeLinkPos(EntryPos);
4658  }
4659  NextTrackVectorPosition = Track->TrackElementAt(392, CurrentTrackVectorPosition).Conn[ExitPos];
4660  NextEntryPos = Track->TrackElementAt(393, CurrentTrackVectorPosition).ConnLinkPos[ExitPos];
4661  CurrentTrackVectorPosition = NextTrackVectorPosition;
4662  EntryPos = NextEntryPos;
4663  }
4664  if(ReturnVal == 1)
4665  {
4666  Utilities->CallLogPop(708);
4667  return(false);
4668  }
4669  if(ReturnVal == 2)
4670  {
4671  Utilities->CallLogPop(709);
4672  return(true);
4673  }
4674  if(ReturnVal == 3)
4675  {
4676  Utilities->CallLogPop(946);
4677  return(true);
4678  }
4679  if(ReturnVal == 4)
4680  {
4681  Utilities->CallLogPop(947);
4682  return(true);
4683  }
4684  else
4685  {
4686  throw Exception("Error - failed to set ReturnVal in ClearToNextSignal()");
4687  }
4688 }
4689 
4690 // ---------------------------------------------------------------------------
4691 
4693 /*
4694  Check whether calling-on conditions met - a) approaching train has stopped at a signal but not at a location;
4695  b) if there is a facing train at the station, it is being held appropriately (must be awaiting a join (Fjo or jbo) or a
4696  change of direction (cdt), remaining here (Frh), or under signaller control);
4697  c) at least 1 platform available for the approaching train; d) points (if any) set for direct route into platform;
4698  e) approaching train is to stop at station; f) no more facing signals between train and platform; g) [dropped g]
4699  h) train in front preventing route being set far enough to release stop signal; i) train in front not exiting at continuation; j) signal must be within 4km of
4700  the stop platform; k) [dropped (k), now can set a reoute or part route into platform so can set points more easily.] l) no existing route conflicts with the route into the platform,
4701  m) not failed or stopped without power
4702  If all OK & route or part route not already set then set an unrestricted route into the station (just to the first platform), to prevent point changing or other route conflicts - if a partial route set than can still
4703  change points outside the route or have a route conflict if another route is set.
4704 */{
4705  if(Track->RouteFlashFlag || TrainFailed || StoppedWithoutPower) //failed & no power conditions added at v2.10.0 as a result of Albie Vowles error
4706  //notified by email on 02/08/21
4707  {
4708  return(false); // don't want to create a new route from the stop signal if one is already in construction & can't call on if failed or no power
4709  }
4710  // some of the callingon route elements may be involved
4711  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CallingOnAllowed" + "," + HeadCode);
4712  bool PlatformFoundFlag = false, StopRequired = false, SkipRouteCheck = false, RouteOrPartRouteSet = false; // last added at v1.2.0
4713  int CurrentTrackVectorPosition = LeadElement, NextTrackVectorPosition, ElementNumber = 0, Distance = 0;
4714  int RouteStartPosition;
4715  // this is the track vector position of the start element for the new unrestricted route - one past the stop signal
4716  int PlatformPosition;
4717  // the track vector position of the first stop platfrom
4718  int EntryPos = LeadEntryPos, ExitPos, NextEntryPos, RouteID;
4719  // not used here
4720  AnsiString LeadStationName = Track->TrackElementAt(395, LeadElement).ActiveTrackElementName; // still OK even if ""
4721  int LeadElementDistance = Track->TrackElementAt(1017, LeadElement).Length01; //added after 2.7.0 as don't want to add this to overall distance since train has already covered this distance
4722  // use Length01, may be wrong for points/crossovers/bridges but unlikely to occur in practice
4723  // must be stopped at a signal but not at a location & still in timetable (a)
4725  // no need to check for SignallerStopped as this function only called in Timetable mode
4726  {
4727  Utilities->CallLogPop(711);
4728  return(false);
4729  }
4730  while(true)
4731  {
4732  TTrackElement &CurrentTrackElement = Track->TrackElementAt(396, CurrentTrackVectorPosition);
4733  // don't look further than 4km ahead (j)
4734  if(Distance > (4000 + LeadElementDistance))
4735  {
4736  Utilities->CallLogPop(967);
4737  return(false);
4738  }
4739  // if find another train on an element in front, before find a valid platform, return false (c)
4740  if((CurrentTrackElement.TrainIDOnElement > -1) && (CurrentTrackElement.TrainIDOnElement != TrainID) && !PlatformFoundFlag)
4741  {
4742  Utilities->CallLogPop(713);
4743  return(false);
4744  }
4745  // if find another train in front when there is a valid platform (keep searching after find a platform as train may still
4746  // be facing later on)
4747  if((CurrentTrackElement.TrainIDOnElement > -1) && (CurrentTrackElement.TrainIDOnElement != TrainID) && PlatformFoundFlag)
4748  {
4749  // get LeadElement, if -1 return (could be exiting at continuation) (i)
4750  TTrain OtherTrain = TrainController->TrainVectorAtIdent(12, CurrentTrackElement.TrainIDOnElement);
4751  if(OtherTrain.LeadElement == -1)
4752  {
4753  Utilities->CallLogPop(714);
4754  return(false);
4755  }
4756  // if a facing train then make sure it is awaiting a join (Fjo or jbo) or a change of direction (cdt), or remaining here (Frh) (b)
4757  if(OtherTrain.LeadElement == CurrentTrackVectorPosition)
4758  {
4759  AnsiString OtherCommand = OtherTrain.ActionVectorEntryPtr->Command;
4760  if((OtherCommand == "Fjo") || (OtherCommand == "jbo") || (OtherCommand == "cdt") || (OtherCommand == "Frh") ||
4761  (OtherTrain.TrainMode == Signaller))
4762  {
4763  break;
4764  }
4765  else
4766  {
4767  Utilities->CallLogPop(955);
4768  return(false);
4769  }
4770  }
4771  else // (h)
4772  {
4773  break;
4774  }
4775  }
4776  // if reach buffers or exit continuation return false (can set route)
4777  if(((CurrentTrackElement.TrackType == Buffers) || (CurrentTrackElement.TrackType == Continuation)) && (EntryPos == 1))
4778  {
4779  Utilities->CallLogPop(716);
4780  return(false);
4781  }
4782  // if reach forward signal (other than the one the train is waiting at) return false (can set route) (f)
4783  if((EntryPos < 2) && (CurrentTrackElement.Config[1 - EntryPos] == Signal) && (CurrentTrackVectorPosition != Track->TrackElementAt(404,
4785  {
4786  Utilities->CallLogPop(717);
4787  return(false);
4788  }
4789  // if reach a location that isn't in timetable return false - drop this as still can't set a route
4790 /*
4791  if((Track->TrackElementAt(405, CurrentTrackVectorPosition).ActiveTrackElementName != "") && (Track->TrackElementAt(406, CurrentTrackVectorPosition).ActiveTrackElementName != LeadStationName) &&
4792  (NameInTimetableBeforeCDT(14, Track->TrackElementAt(407, CurrentTrackVectorPosition).ActiveTrackElementName) == -1))
4793  {
4794  Utilities->CallLogPop(718);
4795  return false;
4796  }
4797 */
4798  // if reach a location that is in timetable set PlatformFoundFlag (but not if position is points set to diverge) (e)
4799  if((CurrentTrackElement.ActiveTrackElementName != "") && (CurrentTrackElement.ActiveTrackElementName != LeadStationName) &&
4800  (NameInTimetableBeforeCDT(15, CurrentTrackElement.ActiveTrackElementName, StopRequired) > -1))
4801  {
4802  if(StopRequired)
4803  {
4804  if((CurrentTrackElement.TrackType != Points) || ((CurrentTrackElement.TrackType == Points) && (CurrentTrackElement.Attribute == 0)))
4805  {
4806  if(!PlatformFoundFlag)
4807  {
4808  PlatformPosition = CurrentTrackVectorPosition;
4809  }
4810  // ensure this only set once at first valid platform position - the unrestricted route will end here
4811  PlatformFoundFlag = true;
4812  }
4813  }
4814  }
4815  // Drop this below - was to prevent call-on if front train had left the station. Criterion now is not that front
4816  // train has to be at station but that has to be before the next forward signal
4817 /*
4818  if((Track->TrackElementAt(411, CurrentTrackVectorPosition).ActiveTrackElementName == "") && (PlatformFoundFlag))
4819  {
4820  Utilities->CallLogPop(719);
4821  return false;
4822  }
4823 */
4824  // make sure points are followed correctly (d) & set ExitPos
4825  if(CurrentTrackElement.TrackType == Points)
4826  {
4827  if((EntryPos == 0) || (EntryPos == 2))
4828  {
4829  if(CurrentTrackElement.Attribute == 0)
4830  {
4831  ExitPos = 1;
4832  }
4833  else
4834  {
4835  ExitPos = 3;
4836  }
4837  }
4838  if(EntryPos == 1)
4839  {
4840  if(CurrentTrackElement.Attribute == 0)
4841  {
4842  ExitPos = 0;
4843  }
4844  else
4845  {
4846  Utilities->CallLogPop(720);
4847  return(false);
4848  }
4849  }
4850  if(EntryPos == 3)
4851  {
4852  if(CurrentTrackElement.Attribute == 1)
4853  {
4854  ExitPos = 0;
4855  }
4856  else
4857  {
4858  Utilities->CallLogPop(721);
4859  return(false);
4860  }
4861  }
4862  }
4863  else
4864  {
4865  ExitPos = Track->GetNonPointsOppositeLinkPos(EntryPos);
4866  }
4867  // check existing routes - if element forward of the signal (ElementNumber == 2) is AutoSignals then OK without further checks as this route must extend to
4868  // the next signal so must at least reach the station, also if have another route set (must be unrestricted) from either the stop signal or the element after it
4869  // to or towards the platform (& all points set correctly) then OK, otherwise reject if (1) there are any route elements already set from element
4870  // forward of element after the signal to & including the first platform element (covers crossover with other route set) or (2) a fouled diagonal (k)
4871  if(ElementNumber < 2)
4872  {
4873  SkipRouteCheck = true;
4874  }
4875  else
4876  {
4877  SkipRouteCheck = false;
4878  }
4879  if(ElementNumber == 1) // the stop signal
4880  {
4881  RouteStartPosition = CurrentTrackVectorPosition;
4882  }
4883 /*
4884  if(ElementNumber == 2)
4885  {
4886  if(AllRoutes->GetRouteTypeAndNumber(18, CurrentTrackVectorPosition, EntryPos, RouteID) == AllRoutes->AutoSigsRoute) AutoSigs = true;
4887  else AutoSigs = false;
4888  if(AllRoutes->GetRouteTypeAndNumber(25, CurrentTrackVectorPosition, EntryPos, RouteID) == AllRoutes->NotAutoSigsRoute) OtherFullRouteSet = true;
4889  }
4890 */
4891  if(ElementNumber > 1)
4892  {
4893  if(AllRoutes->GetRouteTypeAndNumber(26, CurrentTrackVectorPosition, EntryPos, RouteID) != AllRoutes->NoRoute)
4894  {
4895  RouteOrPartRouteSet = true;
4896  }
4897  else
4898  {
4899  RouteOrPartRouteSet = false;
4900  }
4901  }
4902  if(!SkipRouteCheck && !RouteOrPartRouteSet)
4903  {
4904  if(AllRoutes->TrackIsInARoute(16, CurrentTrackVectorPosition, EntryPos)) // must be a conflicting route
4905  {
4906  Utilities->CallLogPop(1859);
4907  return(false);
4908  }
4909  int ExitLink = CurrentTrackElement.Link[ExitPos];
4910  if((ExitLink == 1) || (ExitLink == 3) || (ExitLink == 7) || (ExitLink == 9))
4911  {
4912  if(AllRoutes->DiagonalFouledByRouteOrTrain(6, CurrentTrackElement.HLoc, CurrentTrackElement.VLoc, ExitLink))
4913  {
4914  Utilities->CallLogPop(1850);
4915  return(false);
4916  }
4917  }
4918  }
4919  // finished all checks, now update CurrentTrackVectorPosition & EntryPos for the next iteration
4920  if(EntryPos < 2)
4921  {
4922  Distance += CurrentTrackElement.Length01;
4923  }
4924  else
4925  {
4926  Distance += CurrentTrackElement.Length23;
4927  }
4928  NextTrackVectorPosition = CurrentTrackElement.Conn[ExitPos];
4929  NextEntryPos = CurrentTrackElement.ConnLinkPos[ExitPos];
4930  CurrentTrackVectorPosition = NextTrackVectorPosition;
4931  EntryPos = NextEntryPos;
4932  ElementNumber++;
4933  } // while(true)
4934 
4935  // if all OK & autosigs route not already set then set an unrestricted route into the station (just to the first platform)
4936  // from the stop signal (note that it may be last in an autosigs route)
4937  // a single element route at the stop signal should have been removed prior to this function being called (that called before
4938  // this in ClockTimer2)
4939 
4940  // now add elements to the CallonVector
4941  TAllRoutes::TCallonEntry CallonEntry(RouteOrPartRouteSet, RouteStartPosition, PlatformPosition);
4942 
4943  AllRoutes->CallonVector.push_back(CallonEntry);
4944  Utilities->CallLogPop(1860);
4945  return(true); // return false if fail to set route for any reason
4946 }
4947 
4948 // ---------------------------------------------------------------------------
4949 /*
4950  bool TTrain::TimetableFinished()
4951  {
4952  if((ActionVectorEntryPtr == TrainDataEntryPtr->ActionVector.end()) || (ActionVectorEntryPtr->FormatType == Repeat))//past all actions
4953  {
4954  return true;
4955  }
4956  return false;
4957  }
4958 */
4959 // ---------------------------------------------------------------------------
4960 
4961 AnsiString TTrain::GetTrainHeadCode(int Caller)
4962 
4963 {
4964  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",GetTrainHeadCode" + "," + HeadCode);
4965  AnsiString RepeatHeadCode = TrainController->GetRepeatHeadCode(0, HeadCode, RepeatNumber, IncrementalDigits);
4966 
4967  Utilities->CallLogPop(1452);
4968  return(RepeatHeadCode);
4969 }
4970 
4971 // ---------------------------------------------------------------------------
4972 
4973 TDateTime TTrain::GetTrainTime(int Caller, TDateTime Time)
4974 {
4975  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",GetTrainTime," + Utilities->Format96HHMMSS(Time));
4976  TDateTime RepeatTime = TrainController->GetRepeatTime(1, Time, RepeatNumber, IncrementalMinutes);
4977 
4978  Utilities->CallLogPop(1453);
4979  return(RepeatTime);
4980 }
4981 
4982 // ---------------------------------------------------------------------------
4983 
4984 bool TTrain::IsThereAnAdjacentTrain(int Caller, TTrain *&TrainToBeJoinedBy)
4985 {
4986  // Used to check for a stopped adjacent train for use in PopUp menu //new at v2.4.0
4987  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",IsThereAnAdjacentTrain" + "," + HeadCode);
4988  // check if there's a stopped adjacent train, if there is but not under sig control give a message in calling function
4989  // first check that train is fully on the railway
4990  bool FrontValid = false, RearValid = false;
4991  TTrackElement FrontAdjacentTrackElement, RearAdjacentTrackElement;
4992 
4993  if((LeadElement == -1) || (MidElement == -1))
4994  {
4995  TrainToBeJoinedBy = NULL;
4996  Utilities->CallLogPop(2131);
4997  return(false);
4998  }
5000  {
5001  FrontAdjacentTrackElement = Track->TrackElementAt(965, (Track->TrackElementAt(966, LeadElement).Conn[LeadExitPos]));
5002  FrontValid = true;
5003  }
5005  {
5006  RearAdjacentTrackElement = Track->TrackElementAt(968, (Track->TrackElementAt(969, MidElement).Conn[MidEntryPos]));
5007  RearValid = true;
5008  }
5009  int TrainToBeJoinedByID = -1;
5010 
5011  // first check if on a 2-track element & select correct ID number if so
5012  if(FrontValid)
5013  {
5014  if(FrontAdjacentTrackElement.TrackType == Bridge)
5015  {
5017  {
5018  TrainToBeJoinedByID = FrontAdjacentTrackElement.TrainIDOnBridgeTrackPos23;
5019  }
5020  else
5021  {
5022  TrainToBeJoinedByID = FrontAdjacentTrackElement.TrainIDOnBridgeTrackPos01;
5023  }
5024  }
5025  else
5026  {
5027  TrainToBeJoinedByID = FrontAdjacentTrackElement.TrainIDOnElement;
5028  }
5029  }
5030  if((TrainToBeJoinedByID < 0) && RearValid)
5031  {
5032  // first check if on a 2-track element & select correct ID number if so
5033  if(RearAdjacentTrackElement.TrackType == Bridge)
5034  {
5036  {
5037  TrainToBeJoinedByID = RearAdjacentTrackElement.TrainIDOnBridgeTrackPos23;
5038  }
5039  else
5040  {
5041  TrainToBeJoinedByID = RearAdjacentTrackElement.TrainIDOnBridgeTrackPos01;
5042  }
5043  }
5044  else
5045  {
5046  TrainToBeJoinedByID = RearAdjacentTrackElement.TrainIDOnElement;
5047  }
5048  }
5049  if(TrainToBeJoinedByID < 0) // no adjacent train
5050  {
5051  TrainToBeJoinedBy = NULL;
5052  Utilities->CallLogPop(2132);
5053  return(false);
5054  }
5055  TrainToBeJoinedBy = &(TrainController->TrainVectorAtIdent(44, TrainToBeJoinedByID));
5056  if(!TrainToBeJoinedBy->Stopped())
5057  {
5058  TrainToBeJoinedBy = NULL;
5059  Utilities->CallLogPop(2133);
5060  return(false);
5061  }
5062  Utilities->CallLogPop(2134);
5063  return(true);
5064 }
5065 
5066 // ---------------------------------------------------------------------------
5067 
5068 void TTrain::LogAction(int Caller, AnsiString OwnHeadCode, AnsiString OtherHeadCode, TActionType ActionType, AnsiString LocationName,
5069  TDateTime TimetableNonRepeatTime, bool Warning)
5070 /*
5071  Time = timetable time, the time adjustments for repeat trains is carried out internally
5072  Not all messages need this, if not needed a dummy value is required but not used
5073 
5074  Arrive: 06:05:40: 2F46 arrived at Old Street 1 minute late
5075  Pass: 06:05:40: 2F46 passed Old Street 1 minute late
5076  Terminate: 06:05:40: 2F46 terminated at Old Street 1 minute late
5077  //NB for Frh just give terminated message but without event time - don't use this function
5078  Depart: 06:05:15: 3F43 departed from Essex Road 2 minutes late
5079  Create: 06:05:40: 2F46 created at Old Street 1 minute late
5080  Enter: 06:05:40: 2F46 entered railway at Old Street 1 minute late
5081  Leave: 06:05:40: 2F46 left railway at 57-N4 1 minute late
5082  FrontSplit: 06:05:40: 2F46 split from front to 3D54 at Old Street 1 minute late
5083  RearSplit: 06:05:40: 2F46 split from rear to 3D54 at Old Street 1 minute late
5084  JoinedByOther: 06:05:40: 2F46 joined by 3D54 at Old Street 1 minute late
5085  ChangeDirection: 06:05:40: 2F46 changed direction at Old Street 1 minute late
5086  NewService: 06:05:40: 2F46 became new service 3D54 at Old Street 1 minute late
5087  TakeManualControl: 06:05:40: 2F46 taken under signaller control at Old Street
5088  RestoreTimetableControl: 06:05:40: 2F46 restored to timetable control at Old Street
5089  RemoveTrain: 06:05:40: 2F46 REMOVED FROM RAILWAY DUE TO CRASH at Old Street
5090  RemoveTrain: 06:05:40: 2F46 REMOVED FROM RAILWAY DUE TO DERAILMENT at Old Street
5091  RemoveTrain: 06:05:40: 2F46 REMOVED FROM RAILWAY at Old Street
5092  SignallerMoveForwards 06:05:40: 2F46 received signaller authority to proceed
5093  SignallerChangeDirection 06:05:40: 2F46 changed direction under signaller control at Old Street
5094  SignallerPassRedSignal 06:05:40: 2F46 received signaller authority to pass red signal
5095  SignallerJoin 06:05:40: 2F46 joined under signaller control by 3D54 at Old Street //new at v2.4.0
5096  TrainFailure 06:05:40: 2F46 suffered an onboard power failure at Old Street //new at v2.4.0
5097  RepairFailedTrain 06:05:40: 2F46 failure repaired at Old Street //new at v2.4.0
5098  SignallerControlStop 06:05:40: 2F46 received signaller instruction to stop
5099  SignallerStop 06:05:40: 2F46 stopped on signaller command
5100  SignallerLeave: 06:05:40: 2F46 left railway under signaller control at 57-N4
5101  SignallerStepForward: 06:05:40: 2F46 received signaller authority to step forward
5102 */{
5103  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",LogAction," + OwnHeadCode + "," + OtherHeadCode + "," +
5104  AnsiString(ActionType) + "," + LocationName + "," + HeadCode);
5105  AnsiString BaseLog = "", WarningBaseLog = "", PerfLog = "", ActionLog = "";
5106  int IntMinsLate = 0;
5107 
5108  // need to set it in case MinsLate == 0, since it isn't tested for that
5109  if(ActionType == Arrive)
5110  {
5111  ActionLog = " arrived at ";
5112  }
5113  if(ActionType == Terminate)
5114  {
5115  if(TerminatedMessageSent) // to avoid it being sent twice
5116  {
5117  Utilities->CallLogPop(1104);
5118  return;
5119  }
5120  ActionLog = " terminated at ";
5121  TerminatedMessageSent = true;
5122  }
5123  if(ActionType == Depart)
5124  {
5125  ActionLog = " departed from ";
5126  }
5127  if(ActionType == Pass)
5128  {
5129  ActionLog = " passed ";
5130  }
5131  if(ActionType == Create)
5132  {
5133  ActionLog = " created at ";
5134  }
5135  if(ActionType == Enter)
5136  {
5137  ActionLog = " entered railway at ";
5138  }
5139  if(ActionType == Leave)
5140  {
5141  ActionLog = " left railway at ";
5142  }
5143  if(ActionType == FrontSplit)
5144  {
5145  ActionLog = " split from front to ";
5146  }
5147  if(ActionType == RearSplit)
5148  {
5149  ActionLog = " split from rear to ";
5150  }
5151  if(ActionType == JoinedByOther)
5152  {
5153  ActionLog = " joined by ";
5154  }
5155  if(ActionType == ChangeDirection)
5156  {
5157  ActionLog = " changed direction at ";
5158  }
5159  if(ActionType == NewService)
5160  {
5161  ActionLog = " became new service ";
5162  }
5163  if(ActionType == TakeSignallerControl)
5164  {
5165  ActionLog = " taken under signaller control at ";
5166  }
5167  if(ActionType == RestoreTimetableControl)
5168  {
5169  ActionLog = " restored to timetable control at ";
5170  }
5171  if(ActionType == RemoveTrain)
5172  {
5173  if(Crashed)
5174  {
5175  ActionLog = " REMOVED FROM RAILWAY DUE TO CRASH at ";
5176  }
5177  else if(Derailed)
5178  {
5179  ActionLog = " REMOVED FROM RAILWAY DUE TO DERAILMENT at ";
5180  }
5181  else
5182  {
5183  ActionLog = " REMOVED FROM RAILWAY at ";
5184  }
5185  }
5186  if(ActionType == SignallerMoveForwards)
5187  {
5188  ActionLog = " received signaller authority to proceed";
5189  }
5190  if(ActionType == SignallerStepForward)
5191  {
5192  ActionLog = " received signaller authority to step forward";
5193  }
5194  if(ActionType == SignallerChangeDirection)
5195  {
5196  ActionLog = " changed direction under signaller control at ";
5197  }
5198  if(ActionType == SignallerPassRedSignal)
5199  {
5200  ActionLog = " received signaller authority to pass red signal";
5201  }
5202  if(ActionType == SignallerControlStop)
5203  {
5204  ActionLog = " received signaller instruction to stop";
5205  }
5206  if(ActionType == SignallerStop)
5207  {
5208  ActionLog = " stopped on signaller instruction ";
5209  }
5210  if(ActionType == SignallerJoin)
5211  {
5212  ActionLog = " joined under signaller control by ";
5213  }
5214  if(ActionType == TrainFailure)
5215  {
5216  ActionLog = " suffered an onboard power failure at ";
5217  }
5218  if(ActionType == RepairFailedTrain)
5219  {
5220  ActionLog = " failure repaired at ";
5221  }
5222  if(ActionType == SignallerLeave)
5223  {
5224  ActionLog = " left railway under signaller control at ";
5225  }
5226  if(OtherHeadCode != "")
5227  {
5228  OtherHeadCode += " at ";
5229  }
5230  TDateTime ActualTime = TrainController->TTClockTime;
5231 
5232  if(Warning)
5233  {
5234  BaseLog = Utilities->Format96HHMMSS(ActualTime) + " WARNING: " + HeadCode + ActionLog + OtherHeadCode + LocationName;
5235  WarningBaseLog = HeadCode + ActionLog + OtherHeadCode + LocationName;
5236  }
5237  else
5238  {
5239  BaseLog = Utilities->Format96HHMMSS(ActualTime) + ": " + HeadCode + ActionLog + OtherHeadCode + LocationName;
5240  }
5241  bool TimePerformance = true;
5242 
5243  if((ActionType == TakeSignallerControl) || (ActionType == RestoreTimetableControl) || (ActionType == RemoveTrain) || (ActionType == SignallerMoveForwards)
5244  || (ActionType == SignallerChangeDirection) || (ActionType == SignallerPassRedSignal) || (ActionType == SignallerControlStop) ||
5245  (ActionType == SignallerStop) || (ActionType == SignallerLeave) || (ActionType == SignallerStepForward) || (ActionType == SignallerJoin) ||
5246  (ActionType == TrainFailure) || (ActionType == RepairFailedTrain))
5247  // SignallerJoin & RepairFailedTrain new at v2.4.0
5248  {
5249  TimePerformance = false;
5250  }
5251  if(TimePerformance)
5252  {
5253  double MinsLate = ((double)(ActualTime - GetTrainTime(1, TimetableNonRepeatTime))) * 1440;
5254  MinsDelayed = float(MinsLate);
5255  if(ActionType == Pass) //added at v2.9.2 to prevent time to act increasing suddenly for early pass times then becoming 'NOW' when stops at signal
5256  {
5257  MinsDelayed = 0;
5258  }
5259  // new v2.2.0 for OpActionPanel, can be positive or negative
5260  if(ActionType == Arrive)
5261  {
5263  }
5264  // since train has just arrived this value is the
5265  // recoverable time available at this stop, so reduce MinsDelayed by this amount to prevent it being
5266  // subtracted from later stop recoverable times.
5267  if(MinsLate < 0)
5268  {
5269  IntMinsLate = int(ceil(MinsLate));
5270  }
5271  if(MinsLate > 0)
5272  {
5273  IntMinsLate = int(floor(MinsLate));
5274  }
5275  if(IntMinsLate == 0)
5276  {
5277  PerfLog = " on time";
5278  }
5279  else if(IntMinsLate == 1)
5280  {
5281  PerfLog = " 1 minute late";
5282  }
5283  else if(IntMinsLate == -1)
5284  {
5285  PerfLog = " 1 minute early";
5286  }
5287  else if(IntMinsLate > 1)
5288  {
5289  PerfLog = " " + AnsiString(IntMinsLate) + " minutes late";
5290  }
5291  else if(IntMinsLate < -1)
5292  {
5293  int PosIntMinsLate = -IntMinsLate;
5294  PerfLog = " " + AnsiString(PosIntMinsLate) + " minutes early";
5295  }
5296  if(LocationName.Pos('-') > 0)
5297  {
5298  PerfLog = "," + PerfLog;
5299  // if a position add a comma to separate vertical position number from number of minutes (better appearance)
5300  }
5301  Display->PerformanceLog(0, BaseLog + PerfLog);
5302  }
5303  else
5304  {
5305  Display->PerformanceLog(1, BaseLog);
5306  }
5307  if(Warning)
5308  {
5309  Display->WarningLog(0, WarningBaseLog);
5310  }
5311  // update statistics
5312  if((ActionType == Arrive) && (IntMinsLate == 0))
5313  {
5315  }
5316  else if((ActionType == Arrive) && (IntMinsLate > 0))
5317  {
5319  TrainController->TotLateArrMins += IntMinsLate;
5320  }
5321  else if((ActionType == Arrive) && (IntMinsLate < 0))
5322  {
5324  TrainController->TotEarlyArrMins += abs(IntMinsLate);
5325  }
5326 
5327  else if((ActionType == Pass) && (IntMinsLate == 0))
5328  {
5330  }
5331  else if((ActionType == Pass) && (IntMinsLate > 0))
5332  {
5334  TrainController->TotLatePassMins += IntMinsLate;
5335  }
5336  else if((ActionType == Pass) && (IntMinsLate < 0))
5337  {
5339  TrainController->TotEarlyPassMins += abs(IntMinsLate);
5340  }
5341 
5342  else if((ActionType == Leave) && (IntMinsLate == 0)) //new at v2.9.1 as had been omitted in error earlier
5343  {
5345  }
5346  else if((ActionType == Leave) && (IntMinsLate > 0))
5347  {
5349  TrainController->TotLateExitMins += IntMinsLate;
5350  }
5351  else if((ActionType == Leave) && (IntMinsLate < 0))
5352  {
5354  TrainController->TotEarlyExitMins += abs(IntMinsLate);
5355  }
5356 
5357  else if((ActionType == Depart) && (IntMinsLate == 0)) //can't depart early
5358  {
5360  }
5361  else if((ActionType == Depart) && (IntMinsLate > 0))
5362  {
5364  TrainController->TotLateDepMins += IntMinsLate;
5365  }
5366  Utilities->CallLogPop(968);
5367 }
5368 
5369 // ---------------------------------------------------------------------------
5370 
5371 void TTrain::TrainHasFailed(int Caller)
5372 {
5373  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",TrainHasFailed," + HeadCode);
5374  if(Crashed || Derailed || DerailPending)
5375  {
5376  TrainFailurePending = false;
5377  Utilities->CallLogPop(2135);
5378  return;
5379  }
5380  AnsiString LocName = "";
5381 
5382  if(LeadElement > -1)
5383  {
5385  }
5386  if((LocName == "") && (MidElement > -1))
5387  {
5389  }
5390  if((LocName == "") && LeadElement > -1)
5391  {
5392  LocName = Track->TrackElementAt(974, LeadElement).ElementID;
5393  }
5394  if((LocName == "") && (MidElement > -1))
5395  {
5396  LocName = Track->TrackElementAt(975, MidElement).ElementID;
5397  }
5398  TrainController->StopTTClockMessage(81, HeadCode + " has suffered an onboard power failure at " + LocName);
5399  TrainFailed = true;
5400  TrainFailurePending = false;
5401  CallingOnFlag = false; //added at v2.10.0
5403  PowerAtRail = 0.08;
5404  AValue = sqrt(2 * PowerAtRail / Mass);
5406  // TrainFailed only called when PlotElements properly set to Lead, Mid & Lag elements
5407  if(Stopped())
5408  {
5409  EntrySpeed = 0;
5410  ExitSpeedHalf = 0;
5411  ExitSpeedFull = 0;
5412  MaxExitSpeed = 0;
5413  BrakeRate = 0;
5414  StoppedWithoutPower = true;
5415  }
5417  LogAction(33, HeadCode, "", TrainFailure, LocName, TDateTime(0), true);
5418  // true for warning, TDateTime not used
5419  Utilities->CallLogPop(2136);
5420 }
5421 
5422 // ---------------------------------------------------------------------------
5423 
5424 void TTrain::FrontTrainSplit(int Caller)
5425 {
5426 /*
5427  Split logic is:- at least one of 4 final train positions must overlap with one of original train positions, & final 4 positions
5428  will maximise the number at the location. Note that this function isn't sophisticated enough to account for trains already at the
5429  location in determining the 4 positions, and will give a failure message if a train obstructs any of the 4 positions. In these
5430  circumstances the other train will need to be moved sufficiently away to release all 4 positions, then the train will split.
5431 */
5432  TrainController->LogEvent("" + AnsiString(Caller) + ",FrontTrainSplit" + "," + HeadCode);
5433  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",FrontTrainSplit" + "," + HeadCode);
5434  if(PowerAtRail < 1)
5435  // new at v2.4.0 (ActionVectorEntryPtr not incremented so can split when power restored
5436  {
5438  {
5439  TrainController->StopTTClockMessage(82, HeadCode + ": A train without power can't split");
5440  }
5442  Utilities->CallLogPop(2137);
5443  return;
5444  }
5445  AnsiString LocationName = Track->TrackElementAt(555, LeadElement).ActiveTrackElementName;
5446 
5447  if(LocationName == "")
5448  {
5449  LocationName = Track->TrackElementAt(837, MidElement).ActiveTrackElementName;
5450  }
5451  int FirstNamedElementPos, SecondNamedElementPos, FirstNamedLinkedElementPos, SecondNamedLinkedElementPos;
5452  int RearTrainRearPosition, RearTrainFrontPosition, RearTrainExitPos;
5453  int FrontTrainRearPosition, FrontTrainFrontPosition, FrontTrainExitPos;
5455 
5456  // determine all positions & exits
5457  if(LocationName != "")
5458  {
5459  // if message given only call at ~5 sec intervals
5461  {
5462  FirstNamedElementPos = LeadElement;
5463  if(!Track->ThisNamedLocationLongEnoughForSplit(0, LocationName, FirstNamedElementPos,
5464  // check if possible with LeadElement as First
5465  SecondNamedElementPos, FirstNamedLinkedElementPos, SecondNamedLinkedElementPos))
5466  {
5467  FirstNamedElementPos = MidElement;
5468  if(!Track->ThisNamedLocationLongEnoughForSplit(1, LocationName, FirstNamedElementPos,
5469  // if not then accept second if possible (though if Lead no good hard to see how Mid could work, but leave in)
5470  SecondNamedElementPos, FirstNamedLinkedElementPos, SecondNamedLinkedElementPos))
5471  {
5473  {
5474  TrainController->LogActionError(6, HeadCode, "", FailLocTooShort, LocationName);
5476  }
5477  Utilities->CallLogPop(1009);
5478  return;
5479  }
5480  }
5481  else
5482  {
5483  // if first is possible then check if all 4 positions at location, and if not try the second
5484  int LeadPosA = FirstNamedElementPos;
5485  int LeadPosB = FirstNamedLinkedElementPos;
5486  int LeadPosC = SecondNamedElementPos;
5487  int LeadPosD = SecondNamedLinkedElementPos;
5488  // count number of positions that are at the location
5489  int LeadNumAtLoc = 0;
5490  if(Track->TrackElementAt(758, LeadPosA).ActiveTrackElementName == LocationName)
5491  {
5492  LeadNumAtLoc++;
5493  }
5494  if(Track->TrackElementAt(759, LeadPosB).ActiveTrackElementName == LocationName)
5495  {
5496  LeadNumAtLoc++;
5497  }
5498  if(Track->TrackElementAt(760, LeadPosC).ActiveTrackElementName == LocationName)
5499  {
5500  LeadNumAtLoc++;
5501  }
5502  if(Track->TrackElementAt(761, LeadPosD).ActiveTrackElementName == LocationName)
5503  {
5504  LeadNumAtLoc++;
5505  }
5506  if(LeadNumAtLoc < 4)
5507  {
5508  FirstNamedElementPos = MidElement;
5509  if(!Track->ThisNamedLocationLongEnoughForSplit(4, LocationName, FirstNamedElementPos, SecondNamedElementPos, FirstNamedLinkedElementPos,
5510  SecondNamedLinkedElementPos)) // restore originals
5511  {
5512  FirstNamedElementPos = LeadPosA;
5513  FirstNamedLinkedElementPos = LeadPosB;
5514  SecondNamedElementPos = LeadPosC;
5515  SecondNamedLinkedElementPos = LeadPosD;
5516  }
5517  else // count number at location
5518  {
5519  int MidNumAtLoc = 0;
5520  if(Track->TrackElementAt(762, FirstNamedElementPos).ActiveTrackElementName == LocationName)
5521  {
5522  MidNumAtLoc++;
5523  }
5524  if(Track->TrackElementAt(763, FirstNamedLinkedElementPos).ActiveTrackElementName == LocationName)
5525  {
5526  MidNumAtLoc++;
5527  }
5528  if(Track->TrackElementAt(764, FirstNamedLinkedElementPos).ActiveTrackElementName == LocationName)
5529  {
5530  MidNumAtLoc++;
5531  }
5532  if(Track->TrackElementAt(765, SecondNamedLinkedElementPos).ActiveTrackElementName == LocationName)
5533  {
5534  MidNumAtLoc++;
5535  }
5536  if(LeadNumAtLoc > MidNumAtLoc)
5537  // change back, else keep new values
5538  {
5539  FirstNamedElementPos = LeadPosA;
5540  FirstNamedLinkedElementPos = LeadPosB;
5541  SecondNamedElementPos = LeadPosC;
5542  SecondNamedLinkedElementPos = LeadPosD;
5543  }
5544  }
5545  }
5546  }
5547  }
5548  else
5549  {
5550  Utilities->CallLogPop(1791);
5551  return;
5552  }
5553  }
5554  else
5555  {
5556  throw Exception("Error - LocationName not set in FrontTrainSplit");
5557  }
5558  // set front & rear train parameters
5559  // need RearTrainRearPosition, RearTrainFrontPosition, RearTrainExitPos, FrontTrainRearPosition, FrontTrainFrontPosition & FrontTrainExitPos;
5560  // have LeadElement & MidElement of train defining its direction, & one or other on FirstNamedElementPos
5561  // have FirstNamedElementPos, SecondNamedElementPos, FirstNamedLinkedElementPos & SecondNamedLinkedElementPos from ThisNamedLocationLongEnoughForSplit.
5562  if(LeadElement == FirstNamedElementPos)
5563  {
5564  if(MidElement == SecondNamedElementPos)
5565  {
5566  FrontTrainFrontPosition = FirstNamedLinkedElementPos;
5567  FrontTrainRearPosition = FirstNamedElementPos;
5568  RearTrainFrontPosition = SecondNamedElementPos;
5569  RearTrainRearPosition = SecondNamedLinkedElementPos;
5570  }
5571  else // MidElement must == FirstNamedLinkedElementPos
5572  {
5573  FrontTrainFrontPosition = SecondNamedLinkedElementPos;
5574  FrontTrainRearPosition = SecondNamedElementPos;
5575  RearTrainFrontPosition = FirstNamedElementPos;
5576  RearTrainRearPosition = FirstNamedLinkedElementPos;
5577  }
5578  }
5579  else // MidElement == FirstNamedElementPos
5580  {
5581  if(LeadElement == SecondNamedElementPos)
5582  {
5583  FrontTrainFrontPosition = SecondNamedLinkedElementPos;
5584  FrontTrainRearPosition = SecondNamedElementPos;
5585  RearTrainFrontPosition = FirstNamedElementPos;
5586  RearTrainRearPosition = FirstNamedLinkedElementPos;
5587  }
5588  else // LeadElement must == FirstNamedLinkedElementPos
5589  {
5590  FrontTrainFrontPosition = FirstNamedLinkedElementPos;
5591  FrontTrainRearPosition = FirstNamedElementPos;
5592  RearTrainFrontPosition = SecondNamedElementPos;
5593  RearTrainRearPosition = SecondNamedLinkedElementPos;
5594  }
5595  }
5596  RearTrainExitPos = -1;
5597  for(int x = 0; x < 4; x++)
5598  {
5599  if(Track->TrackElementAt(584, RearTrainRearPosition).Conn[x] == RearTrainFrontPosition)
5600  {
5601  RearTrainExitPos = x;
5602  break;
5603  }
5604  }
5605  if(RearTrainExitPos == -1)
5606  {
5607  throw Exception("Error - RearTrainRearPosition not linked to RearTrainFrontPosition in FrontTrainSplit");
5608  }
5609  FrontTrainExitPos = -1;
5610  for(int x = 0; x < 4; x++)
5611  {
5612  if(Track->TrackElementAt(585, FrontTrainRearPosition).Conn[x] == FrontTrainFrontPosition)
5613  {
5614  FrontTrainExitPos = x;
5615  break;
5616  }
5617  }
5618  if(FrontTrainExitPos == -1)
5619  {
5620  throw Exception("Error - FrontTrainRearPosition not linked to FrontTrainFrontPosition in FrontTrainSplit");
5621  }
5622  // check no train (apart from self) on any of the 4 elements & fail if so
5623  int TrainIDOnRearOfRearTrain, TrainIDOnFrontOfRearTrain, TrainIDOnRearOfFrontTrain, TrainIDOnFrontOfFrontTrain;
5624  TTrackElement RearMostElement = Track->TrackElementAt(574, RearTrainRearPosition);
5625 
5626  if((RearMostElement.TrackType == Bridge) && (RearTrainExitPos > 1))
5627  {
5628  TrainIDOnRearOfRearTrain = RearMostElement.TrainIDOnBridgeTrackPos23;
5629  }
5630  else if((RearMostElement.TrackType == Bridge) && (RearTrainExitPos < 2))
5631  {
5632  TrainIDOnRearOfRearTrain = RearMostElement.TrainIDOnBridgeTrackPos01;
5633  }
5634  else
5635  {
5636  TrainIDOnRearOfRearTrain = RearMostElement.TrainIDOnElement;
5637  }
5638  // RearTrainFrontPosition = RearMostElement.Conn[RearTrainExitPos];
5639  TrainIDOnFrontOfRearTrain = Track->TrackElementAt(575, RearTrainFrontPosition).TrainIDOnElement;
5640  // can't be a bridge
5641  TrainIDOnRearOfFrontTrain = Track->TrackElementAt(576, FrontTrainRearPosition).TrainIDOnElement;
5642  // can't be a bridge
5643  // FrontTrainFrontPosition = Track->TrackElementAt(578,FrontTrainRearPosition).Conn[FrontTrainExitPos];
5644  TTrackElement FrontMostElement = Track->TrackElementAt(577, FrontTrainFrontPosition);
5645 
5646  if((FrontMostElement.TrackType == Bridge) && (FrontTrainExitPos > 1))
5647  {
5648  TrainIDOnFrontOfFrontTrain = FrontMostElement.TrainIDOnBridgeTrackPos23;
5649  }
5650  else if((FrontMostElement.TrackType == Bridge) && (FrontTrainExitPos < 2))
5651  {
5652  TrainIDOnFrontOfFrontTrain = FrontMostElement.TrainIDOnBridgeTrackPos01;
5653  }
5654  else
5655  {
5656  TrainIDOnFrontOfFrontTrain = FrontMostElement.TrainIDOnElement;
5657  }
5658  if(((TrainIDOnRearOfRearTrain > -1) && (TrainIDOnRearOfRearTrain != TrainID)) || ((TrainIDOnFrontOfRearTrain > -1) && (TrainIDOnFrontOfRearTrain != TrainID)
5659  ) || ((TrainIDOnRearOfFrontTrain > -1) && (TrainIDOnRearOfFrontTrain != TrainID)) ||
5660  ((TrainIDOnFrontOfFrontTrain > -1) && (TrainIDOnFrontOfFrontTrain != TrainID)))
5661  {
5663  {
5666  }
5667  // don't advance ActionVectorEntryPtr as need to keep trying, other train may move off eventually
5668  Utilities->CallLogPop(1010);
5669  return;
5670  }
5672  {
5674  }
5675  // reposition existing rear train, need to do this first for 2 reasons - 1) will likely be in the way of the new front train, and 2)
5676  // the new train will likely cause a reallocation of the TrainVector, and if so the reference to the existing train will be invalidated.
5677  // Hence deal with existing train while it references a valid entry in the vector, but retain the Old ActionVectorEntryPtr in a separate
5678  // variable as it is needed for setting up the new train
5679  TActionVectorEntry *OldActionVectorEntryPtr = ActionVectorEntryPtr;
5680 
5681  UnplotTrain(0);
5682  StartSpeed = 0;
5683  RearStartElement = RearTrainRearPosition;
5684  RearStartExitPos = RearTrainExitPos;
5685  StoppedAtLocation = true;
5686  if((PowerAtRail < 1) && EntrySpeed < 1) // added at v2.4.0
5687  {
5688  StoppedWithoutPower = true;
5689  }
5690  PlotStartPosition(3);
5695 
5696  Mass = Mass / 2;
5697  MaxBrakeRate = MaxBrakeRate / 2;
5698  PowerAtRail = PowerAtRail / 2;
5699  AValue = sqrt(2 * PowerAtRail / Mass);
5700  // shouldn't change but include in case not set earlier
5701 
5702  // create new front train
5703 /*
5704  TrainController::AddTrain(int RearPosition, int FrontPosition, AnsiString HeadCode, int StartSpeed, int Mass,
5705  double MaxRunningSpeed, double MaxBrakeRate, double PowerAtRail, AnsiString ModeStr, TTrainDataEntry *TrainDataEntryPtr,
5706  int RepeatNumber, int IncrementalMinutes, int SignallerSpeed)
5707 */
5708  // same Mass, MaxBrakeRate & PowerAtRail as this train's halved values, and same MaxRunningSpeed as this train
5709  TActionEventType EventType = NoEvent;
5710 
5711  if(!TrainController->AddTrain(0, FrontTrainRearPosition, FrontTrainFrontPosition, OtherHeadCode, 0, Mass, MaxRunningSpeed, MaxBrakeRate, PowerAtRail,
5712  "Timetable", OldActionVectorEntryPtr->LinkedTrainEntryPtr, RepeatNumber, IncrementalMinutes, IncrementalDigits, SignallerMaxSpeed, false, EventType))
5713  // false for SignallerControl
5714  {
5715  Utilities->CallLogPop(1721); // EventType not used here
5716  // if fails either a throw will have been sent in AddTrain or start position failed prob because of
5717  // another train, in which case a message will have been sent to the perf log, also might well clear later
5718  // when other train moves away
5719  return;
5720  }
5721  // Note data in 'this' now probably invalid as there has been a new addition to the TrainVector, so the train is likely to have a new address, hence make no more changes for the current train
5722  // see mods in UpdateTrain for v1.3.2
5723  TrainController->TrainAdded = true;
5724 
5725  TTrainOperatingData &TTOD = OldActionVectorEntryPtr->LinkedTrainEntryPtr->TrainOperatingDataVector.at(RepeatNumber); // this is for the newly created train
5726 
5727  TTOD.TrainID = TrainController->TrainVector.back().TrainID;
5728  TTOD.RunningEntry = Running;
5729  Utilities->CallLogPop(998);
5730 }
5731 
5732 // ---------------------------------------------------------------------------
5733 
5734 void TTrain::RearTrainSplit(int Caller)
5735 {
5736 /*
5737  Split logic is:- at least one of 4 final train positions must overlap with one of original train positions, & final 4 positions
5738  will maximise the number at the location. Note that this function isn't sophisticated enough to account for trains already at the
5739  location in determining the 4 positions, and will give a failure message if a train obstructs any of the 4 positions. In these
5740  circumstances the other train will need to be moved sufficiently away to release all 4 positions, then the train will split.
5741 */
5742  TrainController->LogEvent("" + AnsiString(Caller) + ",RearTrainSplit" + "," + HeadCode);
5743  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",RearTrainSplit" + "," + HeadCode);
5744  if(PowerAtRail < 1)
5745  // new at v2.4.0 (ActionVectorEntryPtr not incremented so can split when power restored
5746  {
5748  {
5749  TrainController->StopTTClockMessage(83, HeadCode + ": A train without power can't split");
5750  }
5752  Utilities->CallLogPop(2138);
5753  return;
5754  }
5755  AnsiString LocationName = Track->TrackElementAt(587, LeadElement).ActiveTrackElementName;
5756 
5757  if(LocationName == "")
5758  {
5759  LocationName = Track->TrackElementAt(838, MidElement).ActiveTrackElementName;
5760  }
5761  int FirstNamedElementPos, SecondNamedElementPos, FirstNamedLinkedElementPos, SecondNamedLinkedElementPos;
5762  int RearTrainRearPosition, RearTrainFrontPosition, RearTrainExitPos;
5763  int FrontTrainRearPosition, FrontTrainFrontPosition, FrontTrainExitPos;
5765 
5766  // determine all positions & exits
5767  if(LocationName != "")
5768  {
5769  // if message given only call at ~5 sec intervals
5771  {
5772  FirstNamedElementPos = LeadElement;
5773  if(!Track->ThisNamedLocationLongEnoughForSplit(2, LocationName, FirstNamedElementPos, SecondNamedElementPos, FirstNamedLinkedElementPos,
5774  SecondNamedLinkedElementPos))
5775  {
5776  FirstNamedElementPos = MidElement;
5777  if(!Track->ThisNamedLocationLongEnoughForSplit(3, LocationName, FirstNamedElementPos, SecondNamedElementPos, FirstNamedLinkedElementPos,
5778  SecondNamedLinkedElementPos))
5779  {
5781  {
5782  TrainController->LogActionError(9, HeadCode, "", FailLocTooShort, LocationName);
5784  }
5785  Utilities->CallLogPop(1013);
5786  return;
5787  }
5788  }
5789  else
5790  {
5791  // if first is possible then check if all 4 positions at location, and if not try the second
5792  int LeadPosA = FirstNamedElementPos;
5793  int LeadPosB = FirstNamedLinkedElementPos;
5794  int LeadPosC = SecondNamedElementPos;
5795  int LeadPosD = SecondNamedLinkedElementPos;
5796  // count number of positions that are at the location
5797  int LeadNumAtLoc = 0;
5798  if(Track->TrackElementAt(767, LeadPosA).ActiveTrackElementName == LocationName)
5799  {
5800  LeadNumAtLoc++;
5801  }
5802  if(Track->TrackElementAt(768, LeadPosB).ActiveTrackElementName == LocationName)
5803  {
5804  LeadNumAtLoc++;
5805  }
5806  if(Track->TrackElementAt(769, LeadPosC).ActiveTrackElementName == LocationName)
5807  {
5808  LeadNumAtLoc++;
5809  }
5810  if(Track->TrackElementAt(770, LeadPosD).ActiveTrackElementName == LocationName)
5811  {
5812  LeadNumAtLoc++;
5813  }
5814  if(LeadNumAtLoc < 4)
5815  {
5816  FirstNamedElementPos = MidElement;
5817  if(!Track->ThisNamedLocationLongEnoughForSplit(5, LocationName, FirstNamedElementPos, SecondNamedElementPos, FirstNamedLinkedElementPos,
5818  SecondNamedLinkedElementPos)) // restore originals
5819  {
5820  FirstNamedElementPos = LeadPosA;
5821  FirstNamedLinkedElementPos = LeadPosB;
5822  SecondNamedElementPos = LeadPosC;
5823  SecondNamedLinkedElementPos = LeadPosD;
5824  }
5825  else // count number at location
5826  {
5827  int MidNumAtLoc = 0;
5828  if(Track->TrackElementAt(771, FirstNamedElementPos).ActiveTrackElementName == LocationName)
5829  {
5830  MidNumAtLoc++;
5831  }
5832  if(Track->TrackElementAt(772, FirstNamedLinkedElementPos).ActiveTrackElementName == LocationName)
5833  {
5834  MidNumAtLoc++;
5835  }
5836  if(Track->TrackElementAt(773, FirstNamedLinkedElementPos).ActiveTrackElementName == LocationName)
5837  {
5838  MidNumAtLoc++;
5839  }
5840  if(Track->TrackElementAt(774, SecondNamedLinkedElementPos).ActiveTrackElementName == LocationName)
5841  {
5842  MidNumAtLoc++;
5843  }
5844  if(LeadNumAtLoc > MidNumAtLoc)
5845  // change back, else keep new values
5846  {
5847  FirstNamedElementPos = LeadPosA;
5848  FirstNamedLinkedElementPos = LeadPosB;
5849  SecondNamedElementPos = LeadPosC;
5850  SecondNamedLinkedElementPos = LeadPosD;
5851  }
5852  }
5853  }
5854  }
5855  }
5856  else
5857  {
5858  Utilities->CallLogPop(1792);
5859  return;
5860  }
5861  }
5862  else
5863  {
5864  throw Exception("Error - LocationName not set in RearTrainSplit");
5865  }
5866  // set front & rear train parameters
5867  // need RearTrainRearPosition, RearTrainFrontPosition, RearTrainExitPos, FrontTrainRearPosition, FrontTrainFrontPosition & FrontTrainExitPos;
5868  // have LeadElement & MidElement of train defining its direction, & one or other on FirstNamedElementPos
5869  // have FirstNamedElementPos, SecondNamedElementPos, FirstNamedLinkedElementPos & SecondNamedLinkedElementPos from ThisNamedLocationLongEnoughForSplit.
5870  if(LeadElement == FirstNamedElementPos)
5871  {
5872  if(MidElement == SecondNamedElementPos)
5873  {
5874  FrontTrainFrontPosition = FirstNamedLinkedElementPos;
5875  FrontTrainRearPosition = FirstNamedElementPos;
5876  RearTrainFrontPosition = SecondNamedElementPos;
5877  RearTrainRearPosition = SecondNamedLinkedElementPos;
5878  }
5879  else // MidElement must == FirstNamedLinkedElementPos
5880  {
5881  FrontTrainFrontPosition = SecondNamedLinkedElementPos;
5882  FrontTrainRearPosition = SecondNamedElementPos;
5883  RearTrainFrontPosition = FirstNamedElementPos;
5884  RearTrainRearPosition = FirstNamedLinkedElementPos;
5885  }
5886  }
5887  else // MidElement == FirstNamedElementPos
5888  {
5889  if(LeadElement == SecondNamedElementPos)
5890  {
5891  FrontTrainFrontPosition = SecondNamedLinkedElementPos;
5892  FrontTrainRearPosition = SecondNamedElementPos;
5893  RearTrainFrontPosition = FirstNamedElementPos;
5894  RearTrainRearPosition = FirstNamedLinkedElementPos;
5895  }
5896  else // LeadElement must == FirstNamedLinkedElementPos
5897  {
5898  FrontTrainFrontPosition = FirstNamedLinkedElementPos;
5899  FrontTrainRearPosition = FirstNamedElementPos;
5900  RearTrainFrontPosition = SecondNamedElementPos;
5901  RearTrainRearPosition = SecondNamedLinkedElementPos;
5902  }
5903  }
5904  RearTrainExitPos = -1;
5905  for(int x = 0; x < 4; x++)
5906  {
5907  if(Track->TrackElementAt(588, RearTrainRearPosition).Conn[x] == RearTrainFrontPosition)
5908  {
5909  RearTrainExitPos = x;
5910  break;
5911  }
5912  }
5913  if(RearTrainExitPos == -1)
5914  {
5915  throw Exception("Error - RearTrainRearPosition not linked to RearTrainFrontPosition in RearTrainSplit");
5916  }
5917  FrontTrainExitPos = -1;
5918  for(int x = 0; x < 4; x++)
5919  {
5920  if(Track->TrackElementAt(589, FrontTrainRearPosition).Conn[x] == FrontTrainFrontPosition)
5921  {
5922  FrontTrainExitPos = x;
5923  break;
5924  }
5925  }
5926  if(FrontTrainExitPos == -1)
5927  {
5928  throw Exception("Error - FrontTrainRearPosition not linked to FrontTrainFrontPosition in RearTrainSplit");
5929  }
5930  // check no train (apart from self) on any of the 4 elements & fail if so
5931  int TrainIDOnRearOfRearTrain, TrainIDOnFrontOfRearTrain, TrainIDOnRearOfFrontTrain, TrainIDOnFrontOfFrontTrain;
5932  TTrackElement RearMostElement = Track->TrackElementAt(590, RearTrainRearPosition);
5933 
5934  if((RearMostElement.TrackType == Bridge) && (RearTrainExitPos > 1))
5935  {
5936  TrainIDOnRearOfRearTrain = RearMostElement.TrainIDOnBridgeTrackPos23;
5937  }
5938  else if((RearMostElement.TrackType == Bridge) && (RearTrainExitPos < 2))
5939  {
5940  TrainIDOnRearOfRearTrain = RearMostElement.TrainIDOnBridgeTrackPos01;
5941  }
5942  else
5943  {
5944  TrainIDOnRearOfRearTrain = RearMostElement.TrainIDOnElement;
5945  }
5946  // RearTrainFrontPosition = RearMostElement.Conn[RearTrainExitPos];
5947  TrainIDOnFrontOfRearTrain = Track->TrackElementAt(591, RearTrainFrontPosition).TrainIDOnElement;
5948  // can't be a bridge
5949  TrainIDOnRearOfFrontTrain = Track->TrackElementAt(592, FrontTrainRearPosition).TrainIDOnElement;
5950  // can't be a bridge
5951  // FrontTrainFrontPosition = Track->TrackElementAt(593,FrontTrainRearPosition).Conn[FrontTrainExitPos];
5952  TTrackElement FrontMostElement = Track->TrackElementAt(594, FrontTrainFrontPosition);
5953 
5954  if((FrontMostElement.TrackType == Bridge) && (FrontTrainExitPos > 1))
5955  {
5956  TrainIDOnFrontOfFrontTrain = FrontMostElement.TrainIDOnBridgeTrackPos23;
5957  }
5958  else if((FrontMostElement.TrackType == Bridge) && (FrontTrainExitPos < 2))
5959  {
5960  TrainIDOnFrontOfFrontTrain = FrontMostElement.TrainIDOnBridgeTrackPos01;
5961  }
5962  else
5963  {
5964  TrainIDOnFrontOfFrontTrain = FrontMostElement.TrainIDOnElement;
5965  }
5966  if(((TrainIDOnRearOfRearTrain > -1) && (TrainIDOnRearOfRearTrain != TrainID)) || ((TrainIDOnFrontOfRearTrain > -1) && (TrainIDOnFrontOfRearTrain != TrainID)
5967  ) || ((TrainIDOnRearOfFrontTrain > -1) && (TrainIDOnRearOfFrontTrain != TrainID)) ||
5968  ((TrainIDOnFrontOfFrontTrain > -1) && (TrainIDOnFrontOfFrontTrain != TrainID)))
5969  {
5971  {
5974  }
5975  // don't advance ActionVectorEntryPtr as need to keep trying, other train may move off eventually
5976  Utilities->CallLogPop(1014);
5977  return;
5978  }
5980  {
5982  }
5983  // reposition existing front train, need to do this first for 2 reasons - 1) will likely be in the way of the new rear train, and 2)
5984  // the new train will likely cause a reallocation of the TrainVector, and if so the reference to the existing train will be invalidated.
5985  // Hence deal with existing train while it references a valid entry in the vector, but retain the Old ActionVectorEntryPtr in a separate
5986  // variable as it is needed for setting up the new train
5987  TActionVectorEntry *OldActionVectorEntryPtr = ActionVectorEntryPtr;
5988 
5989  UnplotTrain(1);
5990  StartSpeed = 0;
5991  RearStartElement = FrontTrainRearPosition;
5992  RearStartExitPos = FrontTrainExitPos;
5993  StoppedAtLocation = true;
5994  if((PowerAtRail < 1) && EntrySpeed < 1) // added at v2.4.0
5995  {
5996  StoppedWithoutPower = true;
5997  }
5998  PlotStartPosition(4);
6003  Mass = Mass / 2;
6004  // TrainDataEntryPtr->TrainOperatingDataVector.at(RepeatNumber).Mass = Mass;
6005  MaxBrakeRate = MaxBrakeRate / 2;
6006  // TrainDataEntryPtr->TrainOperatingDataVector.at(RepeatNumber).MaxBrakeRate = MaxBrakeRate;
6007  PowerAtRail = PowerAtRail / 2;
6008  // TrainDataEntryPtr->TrainOperatingDataVector.at(RepeatNumber).PowerAtRail = PowerAtRail;
6009  AValue = sqrt(2 * PowerAtRail / Mass);
6010  // shouldn't change but include in case not set earlier
6011 
6012  // create new rear train
6013 /*
6014  TrainController::AddTrain(int RearPosition, int FrontPosition, AnsiString HeadCode, int StartSpeed, int Mass,
6015  double MaxRunningSpeed, double MaxBrakeRate, double PowerAtRail, AnsiString ModeStr, TTrainDataEntry *TrainDataEntryPtr,
6016  int RepeatNumber, int IncrementalMinutes)
6017 */
6018  // same Mass, MaxBrakeRate & PowerAtRail as this train's halved values, and same MaxRunningSpeed as this train
6019  TActionEventType EventType = NoEvent;
6020 
6021  if(!TrainController->AddTrain(1, RearTrainRearPosition, RearTrainFrontPosition, OtherHeadCode, 0, Mass, MaxRunningSpeed, MaxBrakeRate, PowerAtRail,
6022  "Timetable", OldActionVectorEntryPtr->LinkedTrainEntryPtr, RepeatNumber, IncrementalMinutes, IncrementalDigits, SignallerMaxSpeed, false, EventType))
6023  // false for SignallerControl
6024  {
6025  Utilities->CallLogPop(1722); // EventType not used here
6026  // if fails either a throw will have been sent in AddTrain or start position failed prob because of
6027  // another train, in which case a message will have been sent to the perf log, also might well clear later
6028  // when other train moves away
6029  return;
6030  }
6031  // Note data in 'this' now probably invalid as there has been a new addition to the TrainVector, so the train is likely to have a new address, hence make no more changes for the current train
6032  // see mods in UpdateTrain for v1.3.2
6033  TrainController->TrainAdded = true;
6034  TTrainOperatingData &TTOD = OldActionVectorEntryPtr->LinkedTrainEntryPtr->TrainOperatingDataVector.at(RepeatNumber); // this is for the newly created train
6035 
6036  TTOD.TrainID = TrainController->TrainVector.back().TrainID;
6037  TTOD.RunningEntry = Running;
6038  Utilities->CallLogPop(1015);
6039 }
6040 
6041 // ---------------------------------------------------------------------------
6042 
6043 void TTrain::FinishJoin(int Caller)
6044 {
6045  if(FinishJoinLogSent == false)
6046  {
6047  TrainController->LogEvent("" + AnsiString(Caller) + ",FinishJoin" + "," + HeadCode);
6048  FinishJoinLogSent = true; // so don't keep logging this event
6049  // don't need to reset it to false after the event as the train is deleted
6050  }
6051  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",FinishJoin" + "," + HeadCode);
6052  if(TrainFailed)
6053  // new at v2.4.0 (ActionVectorEntryPtr not incremented so can join when repaired) Can FinishJoin if zero power & not failed, as for empty stock
6054  {
6056  {
6057  TrainController->StopTTClockMessage(84, HeadCode + ": A failed train can't join another under timetable control");
6058  }
6060  Utilities->CallLogPop(2139);
6061  return;
6062  }
6063  if(TrainGone)
6064  // this means that the train has already joined the other & is awaiting deletion by TrainController
6065  // without this the 'waiting' message can be given since the other train's ActionVectorEntryPtr has moved
6066  // on from jbo & TrainToJoinIsAdjacent returns false
6067  {
6068  Utilities->CallLogPop(1035);
6069  return;
6070  }
6071  TTrain *TrainToJoin;
6073 
6074  if(!TrainToJoinIsAdjacent(0, TrainToJoin))
6075  {
6077  {
6078  // Display->PerformanceLog(2, TrainController->TTClockTime.FormatString("hh:nn:ss") + ": " + HeadCode + " waiting to join " + JBOHeadCode + " at " + ActionVectorEntryPtr->LocationName);
6081  }
6082  Utilities->CallLogPop(1030);
6083  return; // keep this here in case need to add code before final return
6084  }
6085  // no need to clear error report flag here, cleared in jbo function
6086  // No need to set TimetableFinished, done in jbo function
6087  Utilities->CallLogPop(1031);
6088 }
6089 
6090 // ---------------------------------------------------------------------------
6091 
6092 void TTrain::JoinedBy(int Caller)
6093 {
6094  TrainController->LogEvent("" + AnsiString(Caller) + ",JoinedBy" + "," + HeadCode);
6095  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",JoinedBy" + "," + HeadCode);
6096  if(PowerAtRail < 1)
6097  // new at v2.4.0 (ActionVectorEntryPtr not incremented so can join when power restored)
6098  {
6100  {
6101  TrainController->StopTTClockMessage(85, HeadCode + ": A train without power can't be joined by another under timetable control");
6102  }
6104  Utilities->CallLogPop(2140);
6105  return;
6106  }
6107  TTrain *TrainToBeJoinedBy;
6109 
6110  if(!TrainToBeJoinedByIsAdjacent(0, TrainToBeJoinedBy))
6111  {
6113  {
6114  // Display->PerformanceLog(3, TrainController->TTClockTime.FormatString("hh:nn:ss") + ": " + HeadCode + " waiting to be joined by " + FJOHeadCode + " at " + ActionVectorEntryPtr->LocationName);
6117  }
6118  LastActionDelayFlag = true;
6119  // need to update LastActionTime if this train first to arrive as need 30s after
6120  // both adjacent before the join
6121  Utilities->CallLogPop(1032);
6122  return;
6123  }
6124  // here when other train is adjacent
6126  {
6128  // need to update this as need 30s after both adjacent before the join
6129  LastActionDelayFlag = false;
6130  Utilities->CallLogPop(1033);
6131  return;
6132  }
6133  // here when other train is adjacent & 30 secs elapsed since both adjacent
6134 
6135  // set new values for mass etc
6136  if(MaxRunningSpeed > TrainToBeJoinedBy->MaxRunningSpeed)
6137  {
6138  MaxRunningSpeed = TrainToBeJoinedBy->MaxRunningSpeed;
6139  }
6140  double OtherBrakeForce = TrainToBeJoinedBy->MaxBrakeRate * TrainToBeJoinedBy->Mass;
6141  double OwnBrakeForce = MaxBrakeRate * Mass;
6142  double CombinedBrakeRate = (OtherBrakeForce + OwnBrakeForce) / (TrainToBeJoinedBy->Mass + Mass);
6143 
6144  Mass += TrainToBeJoinedBy->Mass;
6145  MaxBrakeRate = CombinedBrakeRate;
6146  PowerAtRail += TrainToBeJoinedBy->PowerAtRail;
6147  AValue = sqrt(2 * PowerAtRail / Mass);
6148 
6150  TrainToBeJoinedBy->TrainDataEntryPtr->TrainOperatingDataVector.at(RepeatNumber).EventReported = NoEvent;
6151  TrainToBeJoinedBy->TimetableFinished = true;
6152  TrainToBeJoinedBy->TrainGone = true;
6153  // this will cause other train to be deleted
6154  TrainToBeJoinedBy->JoinedOtherTrainFlag = true;
6158  Utilities->CallLogPop(1034);
6159 }
6160 
6161 // ---------------------------------------------------------------------------
6162 
6164 {
6165  TrainController->LogEvent("" + AnsiString(Caller) + ",ChangeTrainDirection" + "," + HeadCode);
6166  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",ChangeTrainDirection" + "," + HeadCode);
6167  if(PowerAtRail < 1)
6168  // new at v2.4.0 (ActionVectorEntryPtr not incremented so can change direction when power restored)
6169  {
6171  {
6172  TrainController->StopTTClockMessage(86, HeadCode + ": A train without power can't change direction under timetable control");
6173  }
6174  ZeroPowerNoCDTMessage = true;
6175  Utilities->CallLogPop(2141);
6176  return;
6177  }
6178  TColor TempColour = BackgroundColour;
6179 
6180  UnplotTrain(2);
6183  StartSpeed = 0;
6184  StoppedAtLocation = true;
6185  PlotStartPosition(1);
6186  PlotTrainWithNewBackgroundColour(27, TempColour, Display);
6187  // plot same as was - should always be pale green
6191 
6192  //now erase a stub route if there is one, added at v2.5.1
6193  //first element of route is now immediately behind the train (i.e. next to MidElement)
6194  if(MidEntryPos >= 0)
6195  {
6196  TTrackElement MidTrackElement = Track->TrackElementAt(996, MidElement);
6197  int FirstRouteElementVecPos = MidTrackElement.Conn[MidEntryPos];
6198  int FirstRouteLinkPos = MidTrackElement.ConnLinkPos[MidEntryPos];
6199  int RouteNumber = -1;
6200  TAllRoutes::TRouteType RouteType = AllRoutes->GetRouteTypeAndNumber(34, FirstRouteElementVecPos, FirstRouteLinkPos, RouteNumber);
6201  if(RouteType == TAllRoutes::NotAutoSigsRoute)
6202  {
6203  TOneRoute &OR = AllRoutes->GetModifiableRouteAt(28, RouteNumber);
6204  TTrackElement TE = Track->TrackElementAt(997, FirstRouteElementVecPos);
6205  if((TE.TrackType != SignalPost) && (TE.TrackType != Continuation)) //all autosigs routes have signalpost or continuation at 0 so they are automatically excluded
6206  {
6207  bool FirstPass = true; //added at v2.8.0
6208  while(OR.PrefDirSize() > 0) //remove the route up to but not including the next facing signal, in case a pref dir route extends to another signal
6209  {
6210  TPrefDirElement PDE = OR.GetFixedPrefDirElementAt(247, 0); //these will change at each element removal because OR is a reference to the real route
6211  int TVPos2 = PDE.GetTrackVectorPosition();
6212  if(FirstPass && (TVPos2 != FirstRouteElementVecPos)) //route is not directed away from cdt train, could be a call-on for another train (added at v2.8.0)
6213  {
6214  break;
6215  }
6216  TTrackElement TE2 = Track->TrackElementAt(998, TVPos2);
6218  {
6219  AllRoutes->RemoveRouteElement(22, TE2.HLoc, TE2.VLoc, PDE.GetELink());
6220  }
6221  else
6222  {
6223  break;
6224  }
6225  FirstPass = false;
6226  }
6227  AllRoutes->RebuildRailwayFlag = true;
6228  // to force ClearandRebuildRailway at next clock tick if not in zoom-out mode, to replot without stub route
6229  }
6230  }
6231  }
6232  Utilities->CallLogPop(1012);
6233 }
6234 
6235 // ---------------------------------------------------------------------------
6236 
6237 void TTrain::NewTrainService(int Caller)
6238 // change to new train, give new service message
6239 {
6240  TrainController->LogEvent("" + AnsiString(Caller) + ",NewTrainService" + "," + HeadCode);
6241  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",NewTrainService" + "," + HeadCode);
6242  if(PowerAtRail < 1)
6243  // new at v2.4.0 (ActionVectorEntryPtr not incremented so can form new service when power restored)
6244  {
6246  {
6247  TrainController->StopTTClockMessage(87, HeadCode + ": A train without power can't form a new service");
6248  }
6250  Utilities->CallLogPop(2142);
6251  return;
6252  }
6254 
6256  UnplotTrain(3);
6259  StartSpeed = 0;
6264  HeadCode = NewHeadCode;
6265  StoppedAtLocation = true;
6266  PlotStartPosition(5);
6268  // pale green
6271  TerminatedMessageSent = false;
6272  Utilities->CallLogPop(1022);
6273 }
6274 
6275 // ---------------------------------------------------------------------------
6276 
6277 void TTrain::RemainHere(int Caller)
6278 {
6279  Utilities->CallLog.push_back(Utilities->TimeStamp() + AnsiString(Caller) + ",RemainHere" + "," + HeadCode);
6280  if(RemainHereLogNotSent) // to prevent repeated logs
6281  {
6282  TrainController->LogEvent(Utilities->TimeStamp() + AnsiString(Caller) + ",RemainHere" + "," + HeadCode);
6283  RemainHereLogNotSent = false;
6284  }
6286  {
6287  Display->PerformanceLog(5, Utilities->Format96HHMMSS(TrainController->TTClockTime) + ": " + HeadCode + " terminated at " +
6290  TerminatedMessageSent = true;
6291  }
6292  TimetableFinished = true;
6293  Utilities->CallLogPop(1023);
6294 }
6295 
6296 // ---------------------------------------------------------------------------
6297 
6298 void TTrain::SendMissedActionLogs(int Caller, int IncNum, TActionVectorEntry *Ptr)
6299 /*
6300  Enter with pointer at next expected action, and IncNum the number by which have to increase the pointer
6301  to reach the action that is valid for the train's current position. i.e. IncNum error messages to be sent
6302  except where an action is a departure, starting at the current value for the pointer
6303  If IncNum is -1, then send messages for all remaining actions, including Fer if present
6304  If IncNum is -2, then send messages for all remaining actions, except Fer if present
6305 */{
6306  if((Ptr->Command == "Snt") && Ptr->SignallerControl)
6307  {
6308  return; // if remove train that starts under signaller control no messages needed
6309 
6310  }
6311  TrainController->LogEvent("" + AnsiString(Caller) + ",SendMissedActionLogs," + AnsiString(IncNum) + "," + HeadCode);
6312  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SendMissedActionLogs," + AnsiString(IncNum) + "," + HeadCode);
6313  if(IncNum > 0)
6314  {
6315  for(int x = 0; x < IncNum; x++)
6316  {
6317  if(x > 0)
6318  {
6319  Ptr++;
6320  }
6321  // arrival - no need to test for termination as this section only covers missed actions up to the
6322  // arrival point - may terminate later but that not missed
6323  if((Ptr->FormatType == TimeLoc) && (Ptr->ArrivalTime > TDateTime(-1)))
6324  {
6326  }
6327  // arrival & departure
6328  if(Ptr->FormatType == TimeTimeLoc)
6329  {
6331  }
6332  // departure
6333  else if((Ptr->FormatType == TimeLoc) && (Ptr->ArrivalTime == TDateTime(-1)))
6334  {
6335  continue; // skip TimeLoc departures, message given for arrivals
6336  }
6337  // pass
6338  else if(Ptr->FormatType == PassTime)
6339  {
6341  }
6342  // split
6343  else if((Ptr->Command == "fsp") || (Ptr->Command == "rsp"))
6344  {
6346  }
6347  // jbo
6348  else if(Ptr->Command == "jbo")
6349  {
6351  }
6352  // Errors - have reached a station stop point (before a cdt) during Train->Update() so intervening actions can't
6353  // be starts, finishes or cdt
6354  else if((Ptr->Command == "Fns") || (Ptr->Command == "Frh") || (Ptr->Command == "Fer") || (Ptr->Command == "Fjo") || (Ptr->Command == "Snt") ||
6355  (Ptr->Command == "Sfs") || (Ptr->Command == "Snt-sh") || (Ptr->Command == "Sns") || (Ptr->Command == "Sns-sh") || (Ptr->Command == "Sns-fsh") ||
6356  (Ptr->Command == "cdt") || (Ptr->Command == "Frh-sh") || (Ptr->Command == "Fns-sh") || (Ptr->Command == "F-nshs") ||
6357  (Ptr->FormatType == Repeat))
6358  {
6359  throw Exception("Error - illegal command in SendMissedActionLogs for IncNum = " + AnsiString(IncNum) + ", and command = " + Ptr->Command);
6360  }
6361  }
6362  }
6363  else
6364  {
6365  bool IncludeFER = false;
6366  if(IncNum == -1)
6367  {
6368  IncludeFER = true;
6369  }
6370  while(true) // finish commands & repeats break out of loop
6371  {
6372  // Fer & excluded - send normal exit log to give minutes late or early - no, have already sent an unexpected exit message
6373  if(!IncludeFER && (Ptr->Command == "Fer"))
6374  {
6375  break;
6376  }
6377  // Fer & included
6378  else if(IncludeFER && (Ptr->Command == "Fer"))
6379  {
6381  break;
6382  }
6383  // Repeat
6384  else if(Ptr->FormatType == Repeat)
6385  {
6386  break;
6387  }
6388  // Fjo
6389  else if(Ptr->Command == "Fjo")
6390  {
6392  break;
6393  }
6394  // Frh
6395  else if(Ptr->Command == "Frh")
6396  {
6398  {
6400  TerminatedMessageSent = true;
6401  }
6402  break;
6403  }
6404  // Frh-sh
6405  else if(Ptr->Command == "Frh-sh")
6406  {
6408  {
6410  TerminatedMessageSent = true;
6411  }
6412  break;
6413  }
6414  // Fns, F-nshs, Fns-sh
6415  else if((Ptr->Command == "Fns") || (Ptr->Command == "F-nshs") || (Ptr->Command == "Fns-sh"))
6416  {
6418  break;
6419  }
6420  // end of breakout actions
6421  // arrival
6422  if((Ptr->FormatType == TimeLoc) && (Ptr->ArrivalTime > TDateTime(-1)))
6423  {
6424  if(IsTrainTerminating(1))
6425  {
6427  TerminatedMessageSent = true;
6428  }
6429  else
6430  {
6432  }
6433  }
6434  // arrival & departure
6435  else if(Ptr->FormatType == TimeTimeLoc)
6436  {
6438  }
6439  // departure
6440  else if((Ptr->FormatType == TimeLoc) && (Ptr->ArrivalTime == TDateTime(-1)))
6441  {
6442  Ptr++;
6443  continue; // skip TimeLoc departures, message given for arrivals
6444  }
6445  // pass
6446  else if(Ptr->FormatType == PassTime)
6447  {
6449  }
6450  // split
6451  else if((Ptr->Command == "fsp") || (Ptr->Command == "rsp"))
6452  {
6454  }
6455  // jbo
6456  else if(Ptr->Command == "jbo")
6457  {
6459  }
6460  // cdt
6461  else if(Ptr->Command == "cdt")
6462  {
6464  }
6465  // Errors
6466  else if((Ptr->Command == "Snt-sh") || (Ptr->Command == "Sfs") || (Ptr->Command == "Sns") || (Ptr->Command == "Sns-sh") ||
6467  (Ptr->Command == "Sns-fsh") || ((Ptr->Command == "Snt") && !Ptr->SignallerControl))
6468  {
6469  throw Exception("Error - illegal command in SendMissedActionLogs for IncNum = " + AnsiString(IncNum) + ", and command = " + Ptr->Command);
6470  }
6471  Ptr++;
6472  }
6473  TimetableFinished = true;
6474  }
6475  Utilities->CallLogPop(1021);
6476 }
6477 
6478 // ---------------------------------------------------------------------------
6479 
6480 bool TTrain::TrainToJoinIsAdjacent(int Caller, TTrain* &TrainToJoin)
6481 // ensure same repeatnumber
6482 {
6483  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",TrainToJoinIsAdjacent" + "," + HeadCode);
6485 
6486  if(TrainToJoinTDEntry->TrainOperatingDataVector.at(RepeatNumber).RunningEntry != Running)
6487  {
6488  Utilities->CallLogPop(1024);
6489  return(false);
6490  }
6491  TrainToJoin = &(TrainController->TrainVectorAtIdent(33, TrainToJoinTDEntry->TrainOperatingDataVector.at(RepeatNumber).TrainID));
6492  if(TrainToJoin->StoppedAtLocation && (TrainToJoin->TrainMode == Timetable) && (TrainToJoin->ActionVectorEntryPtr->Command == "jbo"))
6493  {
6494  if((Track->TrackElementAt(610, LeadElement).Conn[LeadExitPos] == TrainToJoin->LeadElement) || (Track->TrackElementAt(611,
6495  LeadElement).Conn[LeadExitPos] == TrainToJoin->MidElement) || (Track->TrackElementAt(612, MidElement).Conn[MidEntryPos] == TrainToJoin->LeadElement)
6496  || (Track->TrackElementAt(613, MidElement).Conn[MidEntryPos] == TrainToJoin->MidElement))
6497  {
6498  Utilities->CallLogPop(1025);
6499  return(true);
6500  }
6501  }
6502  Utilities->CallLogPop(1026);
6503  return(false);
6504 }
6505 
6506 // ---------------------------------------------------------------------------
6507 
6508 bool TTrain::TrainToBeJoinedByIsAdjacent(int Caller, TTrain* &TrainToBeJoinedBy)
6509 // ensure same repeatnumber
6510 {
6511  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",TrainToBeJoinedByIsAdjacent" + "," + HeadCode);
6512  TTrainDataEntry *TrainToBeJoinedByTDEntry = ActionVectorEntryPtr->LinkedTrainEntryPtr;
6513 
6514  if(TrainToBeJoinedByTDEntry->TrainOperatingDataVector.at(RepeatNumber).RunningEntry != Running)
6515  {
6516  Utilities->CallLogPop(1027);
6517  return(false);
6518  }
6519  TrainToBeJoinedBy = &(TrainController->TrainVectorAtIdent(15, TrainToBeJoinedByTDEntry->TrainOperatingDataVector.at(RepeatNumber).TrainID));
6520  if(TrainToBeJoinedBy->StoppedAtLocation && (TrainToBeJoinedBy->TrainMode == Timetable) && (TrainToBeJoinedBy->ActionVectorEntryPtr->Command == "Fjo"))
6521  {
6522  if((Track->TrackElementAt(614, LeadElement).Conn[LeadExitPos] == TrainToBeJoinedBy->LeadElement) || (Track->TrackElementAt(615,
6523  LeadElement).Conn[LeadExitPos] == TrainToBeJoinedBy->MidElement) || (Track->TrackElementAt(616,
6524  MidElement).Conn[MidEntryPos] == TrainToBeJoinedBy->LeadElement) || (Track->TrackElementAt(617,
6525  MidElement).Conn[MidEntryPos] == TrainToBeJoinedBy->MidElement))
6526  {
6527  Utilities->CallLogPop(1028);
6528  return(true);
6529  }
6530  }
6531  Utilities->CallLogPop(1029);
6532  return(false);
6533 }
6534 
6535 // ---------------------------------------------------------------------------
6536 
6538 {
6539  TrainController->LogEvent("" + AnsiString(Caller) + ",NewShuttleFromNonRepeatService" + "," + HeadCode);
6540  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",NewShuttleFromNonRepeatService" + "," + HeadCode);
6541  if(PowerAtRail < 1)
6542  // new at v2.4.0 (ActionVectorEntryPtr not incremented so can for new service when power restored)
6543  {
6545  {
6546  TrainController->StopTTClockMessage(88, HeadCode + ": A train without power can't form a new service");
6547  }
6549  Utilities->CallLogPop(2143);
6550  return;
6551  }
6552  AnsiString NewHeadCode = ActionVectorEntryPtr->NonRepeatingShuttleLinkHeadCode;
6553 
6555  UnplotTrain(4);
6558  StartSpeed = 0;
6563  HeadCode = NewHeadCode;
6564  IncrementalMinutes = TrainDataEntryPtr->ActionVector.back().RearStartOrRepeatMins;
6565  IncrementalDigits = TrainDataEntryPtr->ActionVector.back().FrontStartOrRepeatDigits;
6566  StoppedAtLocation = true;
6567  PlotStartPosition(6);
6569  // pale green
6572  TerminatedMessageSent = false;
6573  Utilities->CallLogPop(1078);
6574 }
6575 
6576 // ---------------------------------------------------------------------------
6577 
6579 // need to check whether all repeats finished or not
6580 {
6581  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",RepeatShuttleOrRemainHere" + "," + HeadCode);
6582  if(RemainHereLogNotSent) // to prevent repeated logs
6583  {
6584  TrainController->LogEvent("" + AnsiString(Caller) + ",RepeatShuttleOrRemainHere" + "," + HeadCode);
6585  RemainHereLogNotSent = false;
6586  }
6588  // finished all repeats
6589  {
6591  {
6594  TerminatedMessageSent = true;
6595  // no need to clear message as no more actions
6596  }
6597  TimetableFinished = true;
6598  Utilities->CallLogPop(1080);
6599  return;
6600  }
6601  if(PowerAtRail < 1)
6602  // new at v2.4.0 (ActionVectorEntryPtr not incremented so can for new service when power restored)
6603  {
6605  {
6606  TrainController->StopTTClockMessage(89, HeadCode + ": A train without power can't form a new service");
6607  }
6609  Utilities->CallLogPop(2144);
6610  return;
6611  }
6612  int TempRepeatNumber = RepeatNumber + 1;
6613  // need the next repeat value in order to obtain a correct NewHeadCode, but don't increase it
6614  // until after LogAction or the wrong time will be used
6615  AnsiString NewHeadCode = TrainController->GetRepeatHeadCode(6, ActionVectorEntryPtr->OtherHeadCode, TempRepeatNumber, IncrementalDigits);
6616 
6618  RepeatNumber++;
6619  UnplotTrain(5);
6622  StartSpeed = 0;
6627  HeadCode = NewHeadCode;
6628  StoppedAtLocation = true;
6629  PlotStartPosition(7);
6631  // pale green
6634  TerminatedMessageSent = false;
6635  Utilities->CallLogPop(1079);
6636 }
6637 
6638 // ---------------------------------------------------------------------------
6639 
6641 {
6642  TrainController->LogEvent("" + AnsiString(Caller) + ",RepeatShuttleOrNewNonRepeatService" + "," + HeadCode);
6643  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",RepeatShuttleOrNewNonRepeatService" + "," + HeadCode);
6644  if(PowerAtRail < 1)
6645  // new at v2.4.0 (ActionVectorEntryPtr not incremented so can for new service when power restored)
6646  {
6648  {
6649  TrainController->StopTTClockMessage(90, HeadCode + ": A train without power can't form a new service");
6650  }
6652  Utilities->CallLogPop(2145);
6653  return;
6654  }
6656  // finished all repeats
6657  {
6658  AnsiString NewHeadCode = ActionVectorEntryPtr->NonRepeatingShuttleLinkHeadCode;
6660  RepeatNumber = 0;
6661  UnplotTrain(6);
6664  StartSpeed = 0;
6666  TrainDataEntryPtr->TrainOperatingDataVector.at(RepeatNumber).TrainID = TrainID; // but note that RepeatNumber = 0
6667  TrainDataEntryPtr->TrainOperatingDataVector.at(RepeatNumber).RunningEntry = Running; // but note that RepeatNumber = 0
6669  HeadCode = NewHeadCode;
6670  StoppedAtLocation = true;
6671  PlotStartPosition(9);
6675  TerminatedMessageSent = false;
6676  Utilities->CallLogPop(1081);
6677  return;
6678  }
6679  int TempRepeatNumber = RepeatNumber + 1;
6680  // need the next repeat value in order to obtain a correct NewHeadCode, but don't increase it
6681  // until after LogAction or the wrong time will be used
6682  AnsiString NewHeadCode = TrainController->GetRepeatHeadCode(7, ActionVectorEntryPtr->OtherHeadCode, TempRepeatNumber, IncrementalDigits);
6683 
6685  RepeatNumber++;
6686  UnplotTrain(7);
6689  StartSpeed = 0;
6694  HeadCode = NewHeadCode;
6695  StoppedAtLocation = true;
6696  PlotStartPosition(8);
6698  // pale green
6701  TerminatedMessageSent = false;
6702  Utilities->CallLogPop(1082);
6703 }
6704 
6705 // ---------------------------------------------------------------------------
6706 
6708 {
6709  // Search ActionVector from the position after the entry value for Ptr to the end, and return true if find a Finish
6710  // entry before Fer or TimeLoc. No point checking for TimeTimeLoc since at a stop location now so a later TimeTimeLoc
6711  // must be preceded by a TimeLoc departure
6712  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",IsTrainTerminating" + "," + HeadCode);
6713  for(unsigned int x = 1; x < TrainDataEntryPtr->ActionVector.size(); x++)
6714  {
6716  {
6717  if(((ActionVectorEntryPtr + x)->Command == "Fer") || ((ActionVectorEntryPtr + x)->FormatType == TimeLoc))
6718  {
6719  Utilities->CallLogPop(1083);
6720  return(false);
6721  }
6722  else if((ActionVectorEntryPtr + x)->SequenceType == Finish)
6723  {
6724  Utilities->CallLogPop(1084);
6725  return(true);
6726  }
6727  }
6728  }
6729  Utilities->CallLogPop(1085);
6730  return(false);
6731 }
6732 
6733 // ---------------------------------------------------------------------------
6734 
6735 bool TTrain::AbleToMove(int Caller)
6736 {
6737  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",AbleToMove" + "," + HeadCode);
6738  bool Able = true;
6739 
6740  if(Crashed || Derailed || StoppedAtBuffers || StoppedAtSignal || StoppedWithoutPower) // StoppedWithoutPower added at v2.4.0 &
6741  {
6742  // StoppedForTrainInFront removed as tested below
6743  Able = false;
6744  Utilities->CallLogPop(2146); // added v2.4.0
6745  return(Able); // added v2.4.0
6746  }
6747  if(LeadElement > -1)
6748  {
6749  int FrontPos = Track->TrackElementAt(678, LeadElement).Conn[LeadExitPos];
6750  int FrontEntryPos = Track->TrackElementAt(679, LeadElement).ConnLinkPos[LeadExitPos];
6751  if((FrontPos > -1) && (TrainMode == Signaller) && StoppedForTrainInFront)
6752  {
6753  TTrackElement TrackElement = Track->TrackElementAt(680, FrontPos);
6754  if((TrackElement.TrackType != Bridge) && (TrackElement.TrainIDOnElement == -1))
6755  {
6756  Able = true;
6757  StoppedForTrainInFront = false;
6758  }
6759  else if((TrackElement.TrackType == Bridge) && (FrontEntryPos < 2) && (TrackElement.TrainIDOnBridgeTrackPos01 == -1))
6760  {
6761  Able = true;
6762  StoppedForTrainInFront = false;
6763  }
6764  else if((TrackElement.TrackType == Bridge) && (FrontEntryPos > 1) && (TrackElement.TrainIDOnBridgeTrackPos23 == -1))
6765  {
6766  Able = true;
6767  StoppedForTrainInFront = false;
6768  }
6769  }
6770  else
6771  {
6773  {
6774  Able = false;
6775  }
6776  // don't set StoppedAtBuffers as (presumably) StoppedAtLocation & leave it at that
6777  }
6778  }
6779  else // leaving at a continuation so keep going
6780  {
6781  Able = true;
6782  StoppedForTrainInFront = false;
6783  }
6784  Utilities->CallLogPop(1454);
6785  return(Able);
6786 }
6787 
6788 // ---------------------------------------------------------------------------
6789 
6791 {
6792  // first check if a train immediately in front (may have moved there since this train stopped so StoppedForTrainInFront
6793  // won't be set; if there is a train then set StoppedForTrainInFront
6794  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",AbleToMoveButForSignal" + "," + HeadCode);
6795  // addition below for v1.3.2 after Carwyn Thomas fault reported 24/05/15 - need to check if exiting at continuation (LeadElement == -1) as if so fails at VecPos = .....
6796  if(LeadElement == -1) // exiting at continuation
6797  {
6798  Utilities->CallLogPop(2045);
6799  return(false);
6800  }
6801  // end of addition
6802  int VecPos = Track->TrackElementAt(654, LeadElement).Conn[LeadExitPos];
6803  int NextEntryPos = Track->TrackElementAt(655, LeadElement).ConnLinkPos[LeadExitPos];
6804 
6805  if(Track->OtherTrainOnTrack(5, VecPos, NextEntryPos, TrainID))
6806  {
6807  StoppedForTrainInFront = true;
6808  Utilities->CallLogPop(1455);
6809  return(false);
6810  }
6811  else
6812  {
6813  Utilities->CallLogPop(1456);
6815  // StoppedWithoutPower added v2.4.0
6816  }
6817 }
6818 
6819 // ---------------------------------------------------------------------------
6820 
6822 {
6823  // unplots & replots train, which checks for facing signal and sets StoppedAtSignal if req'd
6824  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SignallerChangeTrainDirection" + "," + HeadCode);
6825  TColor TempColour = BackgroundColour;
6826 
6827  UnplotTrain(8);
6830  StartSpeed = 0;
6831  PlotStartPosition(2);
6832  PlotTrainWithNewBackgroundColour(26, TempColour, Display);
6833 
6834  //now erase a stub route if there is one, added at v2.5.1
6835  //first element of route is now immediately behind the train (i.e. next to MidElement)
6836  if(MidEntryPos >= 0)
6837  {
6838  TTrackElement MidTrackElement = Track->TrackElementAt(1000, MidElement);
6839  int FirstRouteElementVecPos = MidTrackElement.Conn[MidEntryPos];
6840  int FirstRouteLinkPos = MidTrackElement.ConnLinkPos[MidEntryPos];
6841  int RouteNumber = -1;
6842  TAllRoutes::TRouteType RouteType = AllRoutes->GetRouteTypeAndNumber(35, FirstRouteElementVecPos, FirstRouteLinkPos, RouteNumber);
6843  if(RouteType == TAllRoutes::NotAutoSigsRoute)
6844  {
6845  TOneRoute &OR = AllRoutes->GetModifiableRouteAt(29, RouteNumber);
6846  TTrackElement TE = Track->TrackElementAt(1001, FirstRouteElementVecPos);
6847  if((TE.TrackType != SignalPost) && (TE.TrackType != Continuation)) //all autosigs routes have signalpost or continuation at 0 so they are automatically excluded
6848  {
6849  bool FirstPass = true; //added at v2.8.0
6850  while(OR.PrefDirSize() > 0) //remove the route up to but not including the next facing signal, in case a route extends to another signal
6851  {
6852  TPrefDirElement PDE = OR.GetFixedPrefDirElementAt(248, 0); //these will change at each element removal because OR is a reference to the real route
6853  int TVPos2 = PDE.GetTrackVectorPosition();
6854  if(FirstPass && (TVPos2 != FirstRouteElementVecPos)) //route is not directed away from cdt train, could be a call-on for another train (added at v2.8.0)
6855  {
6856  break;
6857  }
6858  TTrackElement TE2 = Track->TrackElementAt(1002, TVPos2);
6860  {
6861  AllRoutes->RemoveRouteElement(23, TE2.HLoc, TE2.VLoc, PDE.GetELink());
6862  }
6863  else
6864  {
6865  break;
6866  }
6867  FirstPass = false;
6868  }
6869  AllRoutes->RebuildRailwayFlag = true;
6870  // to force ClearandRebuildRailway at next clock tick if not in zoom-out mode, to replot without stub route
6871  }
6872  }
6873  }
6874  Utilities->CallLogPop(1102);
6875 }
6876 
6877 // ---------------------------------------------------------------------------
6878 
6880 {
6881  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + "," + AnsiString(Ptr - &TrainDataEntryPtr->ActionVector.front()) +
6882  ",FloatingLabelNextString" + "," + HeadCode);
6883  AnsiString RetStr = "", LocationName = "";
6884 
6885  if((Ptr->Command != "") && (Ptr->Command[1] == 'S'))
6886  {
6887  throw Exception("Error - start entry in FloatingLabelNextString");
6888  }
6889  if(Ptr->FormatType == TimeTimeLoc)
6890  {
6891  if(TrainMode == Timetable)
6892  {
6893  if(!TrainAtLocation(0, LocationName) || (LocationName != Ptr->LocationName))
6894  // not arrived yet in tt mode
6895  {
6896  RetStr = "Arrive " + Ptr->LocationName + " at " + Utilities->Format96HHMM(GetTrainTime(2, Ptr->ArrivalTime));
6897  }
6898  else
6899  {
6900  RetStr = "Depart " + Ptr->LocationName + " at " + Utilities->Format96HHMM(GetTrainTime(3, Ptr->DepartureTime));
6901  }
6902  }
6903  else // TrainMode == Signaller
6904  {
6905  if(!DepartureTimeSet) // not arrived yet
6906  {
6907  RetStr = "Arrive " + Ptr->LocationName + " at " + Utilities->Format96HHMM(GetTrainTime(45, Ptr->ArrivalTime));
6908  }
6909  else
6910  {
6911  RetStr = "Depart " + Ptr->LocationName + " at " + Utilities->Format96HHMM(GetTrainTime(36, Ptr->DepartureTime));
6912  }
6913  }
6914  }
6915  else if((Ptr->FormatType == TimeLoc) && (Ptr->ArrivalTime != TDateTime(-1)))
6916  {
6917  RetStr = "Arrive " + Ptr->LocationName + " at " + Utilities->Format96HHMM(GetTrainTime(4, Ptr->ArrivalTime));
6918  }
6919  else if((Ptr->FormatType == TimeLoc) && (Ptr->ArrivalTime == TDateTime(-1)))
6920  {
6921  RetStr = "Depart " + Ptr->LocationName + " at " + Utilities->Format96HHMM(GetTrainTime(5, Ptr->DepartureTime));
6922  }
6923  else if(Ptr->FormatType == PassTime) // new
6924  {
6925  RetStr = "Pass " + Ptr->LocationName + " at " + Utilities->Format96HHMM(GetTrainTime(31, Ptr->EventTime));
6926  }
6927  else if(Ptr->Command == "Fns")
6928  {
6929  RetStr = "Forms new service " + TrainController->GetRepeatHeadCode(8, Ptr->OtherHeadCode, RepeatNumber, IncrementalDigits) + " at " +
6930  Ptr->LocationName + " at " + Utilities->Format96HHMM(GetTrainTime(6, Ptr->EventTime));
6931  RetStr = CheckNewServiceDepartureTime(0, Ptr, RepeatNumber, Ptr->LinkedTrainEntryPtr, RetStr); //if there is a next service this adds the new service departure time to RetStr
6932  }
6933  else if(Ptr->Command == "F-nshs")
6934  {
6935  RetStr = "Forms new service " + Ptr->NonRepeatingShuttleLinkHeadCode + " at " + Ptr->LocationName + " at " +
6937  RetStr = CheckNewServiceDepartureTime(1, Ptr, 0, Ptr->LinkedTrainEntryPtr, RetStr); //if there is a next service this adds the new service departure time to RetStr
6938  //note that use LinkedTrainEntryPtr and not NonRepeatingShuttleLinkEntryPtr because the forward link from the feeder is LinkedTrainEntryPtr.
6939  //NonRepeatingShuttleLinkEntryPtr is in the shuttle's ActionVector to point back to the feeder.
6940  //NonRepeatingShuttleLinkEntryPtr is used below from the last shuttle as the forward link to the finishing service
6941  }
6942  else if((Ptr->Command == "Fns-sh") && (RepeatNumber < (TrainDataEntryPtr->NumberOfTrains - 1))) // not last repeat number
6943  {
6944  RetStr = "Forms new service " + TrainController->GetRepeatHeadCode(9, Ptr->OtherHeadCode, RepeatNumber + 1, IncrementalDigits) + " at " +
6945  Ptr->LocationName + " at " + Utilities->Format96HHMM(GetTrainTime(7, Ptr->EventTime));
6946  // use RepeatNumber+1 as it's the repeat number of the NEXT shuttle service that is relevant
6947  RetStr = CheckNewServiceDepartureTime(2, Ptr, RepeatNumber + 1, Ptr->LinkedTrainEntryPtr, RetStr); //if there is a next service this adds the new service departure time to RetStr
6948  }
6949  else if((Ptr->Command == "Fns-sh") && (RepeatNumber >= (TrainDataEntryPtr->NumberOfTrains - 1))) // last repeat number
6950  {
6951  RetStr = "Forms new service " + Ptr->NonRepeatingShuttleLinkHeadCode,
6952  +" at " + Ptr->LocationName + " at " + Utilities->Format96HHMM(GetTrainTime(8, Ptr->EventTime));
6953  RetStr = CheckNewServiceDepartureTime(3, Ptr, 0, Ptr->NonRepeatingShuttleLinkEntryPtr, RetStr); //if there is a next service this adds the new service departure time to RetStr
6954  }
6955  else if((Ptr->Command == "Frh-sh") && (RepeatNumber < (TrainDataEntryPtr->NumberOfTrains - 1))) // not last repeat number
6956  {
6957  RetStr = "Forms new service " + TrainController->GetRepeatHeadCode(10, Ptr->OtherHeadCode, RepeatNumber + 1, IncrementalDigits) + " at " +
6958  Ptr->LocationName + " at " + Utilities->Format96HHMM(GetTrainTime(9, Ptr->EventTime));
6959  // use RepeatNumber+1 as it's the repeat number of the NEXT shuttle service that is relevant
6960  RetStr = CheckNewServiceDepartureTime(4, Ptr, RepeatNumber + 1, Ptr->LinkedTrainEntryPtr, RetStr); //if there is a next service this adds the new service departure time to RetStr
6961  }
6962  else if((Ptr->Command == "Frh-sh") && (RepeatNumber >= (TrainDataEntryPtr->NumberOfTrains - 1))) // last repeat number
6963  {
6964  RetStr ="None, train terminated at " + Ptr->LocationName;
6965  }
6966  else if(Ptr->Command == "Frh")
6967  {
6968  RetStr = "None, train terminated at " + Ptr->LocationName;
6969  }
6970  else if(Ptr->Command == "Fer")
6971  {
6972  AnsiString AllowedExits = "";
6973  RetStr = "Exit railway" + TrainController->GetExitLocationAndAt(1, Ptr->ExitList, AllowedExits) + " at " + Utilities->Format96HHMM(GetTrainTime(10, Ptr->EventTime)) + AllowedExits;
6974  }
6975  else if(Ptr->Command == "Fjo")
6976  {
6977  RetStr = "Join " + TrainController->GetRepeatHeadCode(11, Ptr->OtherHeadCode, RepeatNumber, IncrementalDigits) + " at " + Ptr->LocationName + " at " +
6979  }
6980  else if(Ptr->Command == "jbo")
6981  {
6982  RetStr = "Joined by " + TrainController->GetRepeatHeadCode(12, Ptr->OtherHeadCode, RepeatNumber, IncrementalDigits) + " at " + Ptr->LocationName +
6983  " at " + Utilities->Format96HHMM(GetTrainTime(12, Ptr->EventTime));
6984  }
6985  else if(Ptr->Command == "fsp")
6986  {
6987  RetStr = "Front split to " + TrainController->GetRepeatHeadCode(13, Ptr->OtherHeadCode, RepeatNumber, IncrementalDigits) + " at " + Ptr->LocationName +
6988  " at " + Utilities->Format96HHMM(GetTrainTime(13, Ptr->EventTime));
6989  }
6990  else if(Ptr->Command == "rsp")
6991  {
6992  RetStr = "Rear split to " + TrainController->GetRepeatHeadCode(14, Ptr->OtherHeadCode, RepeatNumber, IncrementalDigits) + " at " + Ptr->LocationName +
6993  " at " + Utilities->Format96HHMM(GetTrainTime(14, Ptr->EventTime));
6994  }
6995  else if(Ptr->Command == "cdt")
6996  {
6997  RetStr = "Change direction at " + Ptr->LocationName + " at " + Utilities->Format96HHMM(GetTrainTime(15, Ptr->EventTime));
6998  }
6999  Utilities->CallLogPop(1124);
7000  return(RetStr);
7001 }
7002 
7003 // ---------------------------------------------------------------------------
7004 
7005 AnsiString TTrain::CheckNewServiceDepartureTime(int Caller, TActionVectorEntry *Ptr, int RptNum, TTrainDataEntry *LinkedTrainDataPtr, AnsiString RetStr)
7006 {
7007  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + "," + AnsiString(Ptr - &TrainDataEntryPtr->ActionVector.front()) + ","
7008  + AnsiString(RptNum) + ",CheckNewServiceDepartureTime," + HeadCode);
7009  AnsiString DepTime = "", EventTime = "";
7010  bool CDTFlag = false; //reports if train changes direction before departs
7011  TActionVector NewServiceAV = LinkedTrainDataPtr->ActionVector;
7012  for(TActionVectorIterator AVI = NewServiceAV.begin(); AVI < NewServiceAV.end(); AVI++)
7013  {
7014  if(AVI->Command == "cdt")
7015  {
7016  CDTFlag = !CDTFlag; //toggles flag - allows for there being more than one cdt before departure
7017  continue;
7018  }
7019  if((AVI->Command == "fsp") || (AVI->Command == "rsp"))
7020  {
7021  EventTime = Utilities->Format96HHMM(TrainController->GetControllerTrainTime(19, AVI->EventTime, RptNum, IncrementalMinutes));
7022  RetStr += "\nNew service splits at " + EventTime;
7023  Utilities->CallLogPop(2234);
7024  return(RetStr);
7025  }
7026  if(AVI->Command == "jbo")
7027  {
7028  EventTime = Utilities->Format96HHMM(TrainController->GetControllerTrainTime(20, AVI->EventTime, RptNum, IncrementalMinutes));
7029  RetStr += "\nNew service joined by " + AVI->OtherHeadCode + " at " + EventTime;
7030  Utilities->CallLogPop(2235);
7031  return(RetStr);
7032  }
7033  if((AVI->FormatType == TimeLoc) && (AVI->DepartureTime > TDateTime(-1))) //departure time set
7034  {
7035  DepTime = Utilities->Format96HHMM(TrainController->GetControllerTrainTime(17, AVI->DepartureTime, RptNum, IncrementalMinutes));
7036  if(CDTFlag)
7037  {
7038  RetStr += "\nNew service changes direction then departs at " + DepTime;
7039  }
7040  else
7041  {
7042  RetStr += "\nNew service departs at " + DepTime;
7043  }
7044  Utilities->CallLogPop(2236);
7045  return(RetStr);
7046  }
7047  }
7048  Utilities->CallLogPop(2208);
7049  return(RetStr); //if reach here then RetStr doesn't change
7050 }
7051 
7052 // ---------------------------------------------------------------------------
7053 
7055 // Enter with Ptr pointing to first action to be listed (i.e. next action)
7056 {
7057  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + "," + AnsiString(Ptr - &TrainDataEntryPtr->ActionVector.front()) +
7058  ",FloatingTimetableString" + "," + HeadCode);
7059  AnsiString RetStr = "", PartStr = "";
7060  int Count = 0;
7061 
7062  if((Ptr->Command != "") && (Ptr->Command[1] == 'S') && (TrainMode == Timetable))
7063  // can start in signaller control so exclude this
7064  {
7065  throw Exception("Error - start entry in FloatingTimetableString");
7066  }
7067  TActionVectorEntry *EntryPtr = Ptr; //used in TimeTime Loc check later
7068  bool FirstPass = true;
7069  Ptr--; // because incremented at start of loop
7070 
7071  // different first TimeTimeLoc display if in signaller control
7072  do
7073  {
7074  Ptr++;
7075  if((Ptr->FormatType == Repeat) || TimetableFinished)
7076  {
7077  break;
7078  }
7079  if((Ptr->FormatType == TimeTimeLoc) && FirstPass)
7080  {
7081  AnsiString TrainLoc = "";
7082  if(TrainMode == Timetable)
7083  {
7084  if(TrainAtLocation(1, TrainLoc) && (TrainLoc == Ptr->LocationName) && (Ptr == EntryPtr)) //added '&& (Ptr == EntryPtr)' at v2.6.0 when allow multiple same location entries
7085  {
7086  PartStr = Utilities->Format96HHMM(GetTrainTime(33, Ptr->DepartureTime)) + ": Depart from " + Ptr->LocationName;
7087  }
7088  else if(Ptr->ArrivalTime == Ptr->DepartureTime)
7089  {
7090  PartStr = Utilities->Format96HHMM(GetTrainTime(34, Ptr->ArrivalTime)) + ": Arrive & depart from " + Ptr->LocationName;
7091  }
7092  else
7093  {
7094  PartStr = Utilities->Format96HHMM(GetTrainTime(16, Ptr->ArrivalTime)) + ": Arrive at " + Ptr->LocationName + '\n' +
7095  Utilities->Format96HHMM(GetTrainTime(17, Ptr->DepartureTime)) + ": Depart from " + Ptr->LocationName;
7096  Count++; // because there are 2 entries
7097  }
7098  }
7099  else // TrainMode == Signaller
7100  {
7101  if(DepartureTimeSet)
7102  {
7103  PartStr = Utilities->Format96HHMM(GetTrainTime(37, Ptr->DepartureTime)) + ": Depart from " + Ptr->LocationName;
7104  }
7105  else if(Ptr->ArrivalTime == Ptr->DepartureTime)
7106  {
7107  PartStr = Utilities->Format96HHMM(GetTrainTime(38, Ptr->ArrivalTime)) + ": Arrive & depart from " + Ptr->LocationName;
7108  }
7109  else
7110  {
7111  PartStr = Utilities->Format96HHMM(GetTrainTime(39, Ptr->ArrivalTime)) + ": Arrive at " + Ptr->LocationName + '\n' +
7112  Utilities->Format96HHMM(GetTrainTime(40, Ptr->DepartureTime)) + ": Depart from " + Ptr->LocationName;
7113  Count++; // because there are 2 entries
7114  }
7115  }
7116  }
7117  else if((Ptr->FormatType == TimeTimeLoc) && !FirstPass)
7118  {
7119  AnsiString TrainLoc = "";
7120  if((TrainAtLocation(2, TrainLoc)) && (TrainLoc == Ptr->LocationName) && (Ptr == EntryPtr)) //added '&& (Ptr == EntryPtr)' at v2.6.0 when allow multiple same location entries
7121  {
7122  PartStr = Utilities->Format96HHMM(GetTrainTime(41, Ptr->DepartureTime)) + ": Depart from " + Ptr->LocationName;
7123  }
7124  else if(Ptr->ArrivalTime == Ptr->DepartureTime)
7125  {
7126  PartStr = Utilities->Format96HHMM(GetTrainTime(42, Ptr->ArrivalTime)) + ": Arrive & depart from " + Ptr->LocationName;
7127  }
7128  else
7129  {
7130  PartStr = Utilities->Format96HHMM(GetTrainTime(43, Ptr->ArrivalTime)) + ": Arrive at " + Ptr->LocationName + '\n' +
7131  Utilities->Format96HHMM(GetTrainTime(44, Ptr->DepartureTime)) + ": Depart from " + Ptr->LocationName;
7132  Count++; // because there are 2 entries
7133  }
7134  }
7135  else if((Ptr->FormatType == TimeLoc) && (Ptr->ArrivalTime != TDateTime(-1)))
7136  {
7137  PartStr = Utilities->Format96HHMM(GetTrainTime(18, Ptr->ArrivalTime)) + ": Arrive at " + Ptr->LocationName;
7138  }
7139  else if((Ptr->FormatType == TimeLoc) && (Ptr->ArrivalTime == TDateTime(-1)))
7140  {
7141  PartStr = Utilities->Format96HHMM(GetTrainTime(19, Ptr->DepartureTime)) + ": Depart from " + Ptr->LocationName;
7142  }
7143  else if(Ptr->FormatType == PassTime) // new
7144  {
7145  PartStr = Utilities->Format96HHMM(GetTrainTime(30, Ptr->EventTime)) + ": Pass " + Ptr->LocationName;
7146  }
7147  else if(Ptr->Command == "Fns")
7148  {
7149  PartStr = Utilities->Format96HHMM(GetTrainTime(20, Ptr->EventTime)) + ": Form new service " + TrainController->GetRepeatHeadCode(15,
7150  Ptr->OtherHeadCode, RepeatNumber, IncrementalDigits) + " at " + Ptr->LocationName;
7151  PartStr = CheckNewServiceDepartureTime(5, Ptr, RepeatNumber, Ptr->LinkedTrainEntryPtr, PartStr); //if there is a next service this adds the new service departure time to PartStr
7152  }
7153  else if(Ptr->Command == "F-nshs")
7154  {
7155  PartStr = Utilities->Format96HHMM(GetTrainTime(35, Ptr->EventTime)) + ": Form new service " + Ptr->NonRepeatingShuttleLinkHeadCode + " at " +
7156  Ptr->LocationName;
7157  PartStr = CheckNewServiceDepartureTime(6, Ptr, 0, Ptr->LinkedTrainEntryPtr, PartStr); //if there is a next service this adds the new service departure time to RetStr
7158  //note that use LinkedTrainEntryPtr and not NonRepeatingShuttleLinkEntryPtr because the forward link from the feeder is LinkedTrainEntryPtr.
7159  //NonRepeatingShuttleLinkEntryPtr is in the shuttle's ActionVector to point back to the feeder.
7160  //NonRepeatingShuttleLinkEntryPtr is used below from the last shuttle as the forward link to the finishing service
7161  }
7162  else if((Ptr->Command == "Fns-sh") && (RepeatNumber < (TrainDataEntryPtr->NumberOfTrains - 1))) // not the last repeat number
7163  {
7164  PartStr = Utilities->Format96HHMM(GetTrainTime(21, Ptr->EventTime)) + ": Form new service " + TrainController->GetRepeatHeadCode(16,
7165  Ptr->OtherHeadCode, RepeatNumber + 1, IncrementalDigits) + " at " + Ptr->LocationName;
7166  // use RepeatNumber+1 because it's the repeat number of the NEXT shuttle service that is relevant
7167  PartStr = CheckNewServiceDepartureTime(7, Ptr, RepeatNumber + 1, Ptr->LinkedTrainEntryPtr, PartStr); //if there is a next service this adds the new service departure time to RetStr
7168  }
7169  else if((Ptr->Command == "Fns-sh") && (RepeatNumber >= (TrainDataEntryPtr->NumberOfTrains - 1))) // last repeat number
7170  {
7171  PartStr = Utilities->Format96HHMM(GetTrainTime(22, Ptr->EventTime)) + ": Form new service " + Ptr->NonRepeatingShuttleLinkHeadCode,
7172  +" at " + Ptr->LocationName;
7173  PartStr = CheckNewServiceDepartureTime(8, Ptr, 0, Ptr->NonRepeatingShuttleLinkEntryPtr, PartStr); //if there is a next service this adds the new service departure time to RetStr
7174  }
7175  else if((Ptr->Command == "Frh-sh") && (RepeatNumber < (TrainDataEntryPtr->NumberOfTrains - 1))) // not the last repeat number
7176  {
7177  PartStr = Utilities->Format96HHMM(GetTrainTime(23, Ptr->EventTime)) + ": Form new service " + TrainController->GetRepeatHeadCode(17,
7178  Ptr->OtherHeadCode, RepeatNumber + 1, IncrementalDigits) + " at " + Ptr->LocationName;
7179  // use RepeatNumber+1 because it's the repeat number of the NEXT shuttle service that is relevant
7180  PartStr = CheckNewServiceDepartureTime(9, Ptr, RepeatNumber + 1, Ptr->LinkedTrainEntryPtr, PartStr); //if there is a next service this adds the new service departure time to RetStr
7181  }
7182  else if((Ptr->Command == "Frh-sh") && (RepeatNumber >= (TrainDataEntryPtr->NumberOfTrains - 1))) // last repeat number
7183  {
7184  PartStr = "Terminate at " + Ptr->LocationName;
7185  }
7186  else if(Ptr->Command == "Frh")
7187  {
7188  PartStr = "Terminate at " + Ptr->LocationName;
7189  }
7190  else if(Ptr->Command == "Fer")
7191  {
7192  AnsiString AllowedExits = "";
7193  PartStr = Utilities->Format96HHMM(GetTrainTime(24, Ptr->EventTime)) + ": Exit railway" + TrainController->GetExitLocationAndAt(2, Ptr->ExitList, AllowedExits) + AllowedExits;
7194  }
7195  else if(Ptr->Command == "Fjo")
7196  {
7197  PartStr = Utilities->Format96HHMM(GetTrainTime(25, Ptr->EventTime)) + ": Join " + TrainController->GetRepeatHeadCode(18, Ptr->OtherHeadCode,
7198  RepeatNumber, IncrementalDigits) + " at " + Ptr->LocationName;
7199  }
7200  else if(Ptr->Command == "jbo")
7201  {
7202  PartStr = Utilities->Format96HHMM(GetTrainTime(26, Ptr->EventTime)) + ": Joined by " + TrainController->GetRepeatHeadCode(19, Ptr->OtherHeadCode,
7203  RepeatNumber, IncrementalDigits) + " at " + Ptr->LocationName;
7204  }
7205  else if(Ptr->Command == "fsp")
7206  {
7207  PartStr = Utilities->Format96HHMM(GetTrainTime(27, Ptr->EventTime)) + ": Front split to " + TrainController->GetRepeatHeadCode(20,
7208  Ptr->OtherHeadCode, RepeatNumber, IncrementalDigits) + " at " + Ptr->LocationName;
7209  }
7210  else if(Ptr->Command == "rsp")
7211  {
7212  PartStr = Utilities->Format96HHMM(GetTrainTime(28, Ptr->EventTime)) + ": Rear split to " + TrainController->GetRepeatHeadCode(21,
7213  Ptr->OtherHeadCode, RepeatNumber, IncrementalDigits) + " at " + Ptr->LocationName;
7214  }
7215  else if(Ptr->Command == "cdt")
7216  {
7217  PartStr = Utilities->Format96HHMM(GetTrainTime(29, Ptr->EventTime)) + ": Change direction at " + Ptr->LocationName;
7218  }
7219  if(RetStr != "")
7220  {
7221  RetStr = RetStr + '\n' + PartStr;
7222  }
7223  else
7224  {
7225  RetStr = PartStr;
7226  }
7227  FirstPass = false;
7228  Count++;
7229  }
7230  while(!TimetableFinished && (Count < 32) && ((Ptr->Command == "") || ((Ptr->Command != "") && (Ptr->Command[1] != 'F'))));
7231  // limit of 32 allows a max of 34 entries (33 + 1 for the new service departure time) (may have gone from 32 to 34 because of a TimeTimeLoc), which with track and
7232  // train status gives a max of 48 lines, at 13 pixels each, = 624 pixels & screen height has 641 so will fit comfortably. Also 34 timetable entries is as far
7233  // forward as anyone should wish to see without looking at the full timetable
7234  if(TimetableFinished)
7235  {
7236  if(TrainMode == Timetable)
7237  {
7238  RetStr = "Timetable finished";
7239  }
7240  else
7241  {
7242  RetStr = "No timetable";
7243  }
7244  }
7245  Utilities->CallLogPop(1125);
7246  return(RetStr);
7247 }
7248 
7249 // ---------------------------------------------------------------------------
7250 
7251 void TTrain::SaveOneSessionTrain(int Caller, std::ofstream &OutFile)
7252 {
7253  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SaveOneSessionTrain" + "," + HeadCode);
7254  Utilities->SaveFileString(OutFile, HeadCode);
7257  Utilities->SaveFileInt(OutFile, StartSpeed);
7260  Utilities->SaveFileInt(OutFile, RepeatNumber);
7263  Utilities->SaveFileInt(OutFile, Mass);
7266  Utilities->SaveFileDouble(OutFile, EntrySpeed);
7273  Utilities->SaveFileDouble(OutFile, BrakeRate);
7277  Utilities->SaveFileDouble(OutFile, double(EntryTime));
7278  Utilities->SaveFileDouble(OutFile, double(ExitTimeHalf));
7279  Utilities->SaveFileDouble(OutFile, double(ExitTimeFull));
7280  Utilities->SaveFileDouble(OutFile, double(ReleaseTime));
7281  Utilities->SaveFileDouble(OutFile, double(TRSTime));
7282  Utilities->SaveFileDouble(OutFile, double(LastActionTime));
7286  Utilities->SaveFileInt(OutFile, (short)TrainMode);
7291  Utilities->SaveFileBool(OutFile, Derailed);
7293  Utilities->SaveFileBool(OutFile, Crashed);
7300  Utilities->SaveFileBool(OutFile, NotInService);
7301  Utilities->SaveFileBool(OutFile, Plotted);
7302  Utilities->SaveFileBool(OutFile, TrainGone);
7303  Utilities->SaveFileBool(OutFile, SPADFlag);
7305  Utilities->SaveFileInt(OutFile, HOffset[0]);
7306  Utilities->SaveFileInt(OutFile, HOffset[1]);
7307  Utilities->SaveFileInt(OutFile, HOffset[2]);
7308  Utilities->SaveFileInt(OutFile, HOffset[3]);
7309  Utilities->SaveFileInt(OutFile, VOffset[0]);
7310  Utilities->SaveFileInt(OutFile, VOffset[1]);
7311  Utilities->SaveFileInt(OutFile, VOffset[2]);
7312  Utilities->SaveFileInt(OutFile, VOffset[3]);
7313  Utilities->SaveFileInt(OutFile, PlotElement[0]);
7314  Utilities->SaveFileInt(OutFile, PlotElement[1]);
7315  Utilities->SaveFileInt(OutFile, PlotElement[2]);
7316  Utilities->SaveFileInt(OutFile, PlotElement[3]);
7317  Utilities->SaveFileInt(OutFile, PlotEntryPos[0]);
7318  Utilities->SaveFileInt(OutFile, PlotEntryPos[1]);
7319  Utilities->SaveFileInt(OutFile, PlotEntryPos[2]);
7320  Utilities->SaveFileInt(OutFile, PlotEntryPos[3]);
7322  Utilities->SaveFileInt(OutFile, (short)Straddle);
7323  Utilities->SaveFileInt(OutFile, NextTrainID);
7324  Utilities->SaveFileInt(OutFile, TrainID);
7325  Utilities->SaveFileInt(OutFile, LeadElement);
7326  Utilities->SaveFileInt(OutFile, LeadEntryPos);
7327  Utilities->SaveFileInt(OutFile, LeadExitPos);
7328  Utilities->SaveFileInt(OutFile, MidElement);
7329  Utilities->SaveFileInt(OutFile, MidEntryPos);
7330  Utilities->SaveFileInt(OutFile, MidExitPos);
7331  Utilities->SaveFileInt(OutFile, LagElement);
7332  Utilities->SaveFileInt(OutFile, LagEntryPos);
7333  Utilities->SaveFileInt(OutFile, LagExitPos);
7334  int ColourNumber;
7335 
7337  {
7338  ColourNumber = 0;
7339  }
7341  {
7342  ColourNumber = 1;
7343  }
7345  {
7346  ColourNumber = 2;
7347  }
7349  {
7350  ColourNumber = 3;
7351  }
7353  {
7354  ColourNumber = 4;
7355  }
7357  {
7358  ColourNumber = 5;
7359  }
7361  {
7362  ColourNumber = 6;
7363  }
7365  {
7366  ColourNumber = 7;
7367  }
7369  {
7370  ColourNumber = 8;
7371  }
7373  {
7374  ColourNumber = 9;
7375  }
7377  {
7378  ColourNumber = 10;
7379  }
7381  {
7382  ColourNumber = 11;
7383  }
7385  {
7386  ColourNumber = 12;
7387  }
7388  else if(BackgroundColour == clTRSBackground)
7389  {
7390  ColourNumber = 13;
7391  }
7393  {
7394  ColourNumber = 14; // added at v2.4.0
7395  }
7396  Utilities->SaveFileInt(OutFile, ColourNumber);
7397 
7398  // additional data
7399  bool ForwardHeadCode;
7400 
7401  if(HeadCodePosition[3] == HeadCodeGrPtr[3])
7402  {
7403  ForwardHeadCode = true;
7404  }
7405  // can't use 'if(HeadCodePosition[0] == HeadCodeGrPtr[0])' as HeadCodePosition[0] is set to FrontCodePtr
7406  else
7407  {
7408  ForwardHeadCode = false;
7409  }
7410  Utilities->SaveFileBool(OutFile, ForwardHeadCode);
7411 
7412  int TrainDataEntryValue = TrainDataEntryPtr - &(TrainController->TrainDataVector.at(0));
7413 
7414  Utilities->SaveFileInt(OutFile, TrainDataEntryValue);
7415  int ActionVectorEntryValue = ActionVectorEntryPtr - &(TrainDataEntryPtr->ActionVector.at(0));
7416 
7417  Utilities->SaveFileInt(OutFile, ActionVectorEntryValue);
7418  // now the marker comes next which was ****** originally but used for RestoreTimetableLocation as well some time ago (came before the asterisks)
7419  // but at v2.4.0 need to include StoppedWithoutPower, while keeping length of marker at 6, because that is tested in earlier versions
7420  // so use the last asterisk position for this - 0 for false & 1 for true
7421  // note that failed train data is handled in InterfaceUnit.cpp & stored after the performance file
7422  AnsiString Marker;
7423 
7425  {
7426  Marker = "*****1";
7427  }
7428  else
7429  {
7430  Marker = "*****0";
7431  }
7432  if(RestoreTimetableLocation == "")
7433  {
7434  Utilities->SaveFileString(OutFile, Marker);
7435  }
7436  else
7437  {
7438  AnsiString CombinedString = RestoreTimetableLocation + Marker;
7439  Utilities->SaveFileString(OutFile, CombinedString);
7440  // RestoreTimetableLocation + marker
7441  }
7442  // Note: including RestoreTimetableLocation with the marker is to correct an oversight - it should have been saved earlier
7443  Utilities->CallLogPop(1457);
7444 }
7445 
7446 // ---------------------------------------------------------------------------
7447 
7448 void TTrain::LoadOneSessionTrain(int Caller, std::ifstream &InFile)
7449 {
7450  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",LoadOneSessionTrain"); // don't have headcode yet
7451  HeadCode = Utilities->LoadFileString(InFile);
7454  StartSpeed = Utilities->LoadFileInt(InFile);
7456  if(SignallerMaxSpeed < 10)
7457  {
7458  SignallerMaxSpeed = 10; // added at v0.6 to avoid low max speeds
7459  }
7461  RepeatNumber = Utilities->LoadFileInt(InFile);
7464  Mass = Utilities->LoadFileInt(InFile);
7467  {
7469  }
7470  // above added at v2.1.0 for legacy session files where value may not have been limited
7472  EntrySpeed = Utilities->LoadFileDouble(InFile);
7476  if(TimetableMaxRunningSpeed < 10)
7477  {
7478  TimetableMaxRunningSpeed = 10; // added at v0.6 to avoid low max speeds
7479  }
7481  if(MaxRunningSpeed < 10)
7482  {
7483  MaxRunningSpeed = 10; // added at v0.6 to avoid low max speeds
7484  }
7487  BrakeRate = Utilities->LoadFileDouble(InFile);
7491  EntryTime = TDateTime(Utilities->LoadFileDouble(InFile));
7492  ExitTimeHalf = TDateTime(Utilities->LoadFileDouble(InFile));
7493  ExitTimeFull = TDateTime(Utilities->LoadFileDouble(InFile));
7494  ReleaseTime = TDateTime(Utilities->LoadFileDouble(InFile));
7495  TRSTime = TDateTime(Utilities->LoadFileDouble(InFile));
7496  LastActionTime = TDateTime(Utilities->LoadFileDouble(InFile));
7505  Derailed = Utilities->LoadFileBool(InFile);
7507  Crashed = Utilities->LoadFileBool(InFile);
7514  NotInService = Utilities->LoadFileBool(InFile);
7515  Plotted = Utilities->LoadFileBool(InFile);
7516  TrainGone = Utilities->LoadFileBool(InFile);
7517  SPADFlag = Utilities->LoadFileBool(InFile);
7519  HOffset[0] = Utilities->LoadFileInt(InFile);
7520  HOffset[1] = Utilities->LoadFileInt(InFile);
7521  HOffset[2] = Utilities->LoadFileInt(InFile);
7522  HOffset[3] = Utilities->LoadFileInt(InFile);
7523  VOffset[0] = Utilities->LoadFileInt(InFile);
7524  VOffset[1] = Utilities->LoadFileInt(InFile);
7525  VOffset[2] = Utilities->LoadFileInt(InFile);
7526  VOffset[3] = Utilities->LoadFileInt(InFile);
7527  PlotElement[0] = Utilities->LoadFileInt(InFile);
7528  PlotElement[1] = Utilities->LoadFileInt(InFile);
7529  PlotElement[2] = Utilities->LoadFileInt(InFile);
7530  PlotElement[3] = Utilities->LoadFileInt(InFile);
7531  PlotEntryPos[0] = Utilities->LoadFileInt(InFile);
7532  PlotEntryPos[1] = Utilities->LoadFileInt(InFile);
7533  PlotEntryPos[2] = Utilities->LoadFileInt(InFile);
7534  PlotEntryPos[3] = Utilities->LoadFileInt(InFile);
7536  Straddle = (TStraddle)(Utilities->LoadFileInt(InFile));
7537  NextTrainID = Utilities->LoadFileInt(InFile);
7538  // will be same for all but best to save all anyway
7539  TrainID = Utilities->LoadFileInt(InFile);
7540  LeadElement = Utilities->LoadFileInt(InFile);
7541  LeadEntryPos = Utilities->LoadFileInt(InFile);
7542  LeadExitPos = Utilities->LoadFileInt(InFile);
7543  MidElement = Utilities->LoadFileInt(InFile);
7544  MidEntryPos = Utilities->LoadFileInt(InFile);
7545  MidExitPos = Utilities->LoadFileInt(InFile);
7546  LagElement = Utilities->LoadFileInt(InFile);
7547  LagEntryPos = Utilities->LoadFileInt(InFile);
7548  LagExitPos = Utilities->LoadFileInt(InFile);
7549  int ColourNumber = TColor(Utilities->LoadFileInt(InFile));
7550 
7551  if(ColourNumber == 0)
7552  {
7554  }
7555  else if(ColourNumber == 1)
7556  {
7558  }
7559  else if(ColourNumber == 2)
7560  {
7562  }
7563  else if(ColourNumber == 3)
7564  {
7566  }
7567  else if(ColourNumber == 4)
7568  {
7570  }
7571  else if(ColourNumber == 5)
7572  {
7574  }
7575  else if(ColourNumber == 6)
7576  {
7578  }
7579  else if(ColourNumber == 7)
7580  {
7582  }
7583  else if(ColourNumber == 8)
7584  {
7586  }
7587  else if(ColourNumber == 9)
7588  {
7590  }
7591  else if(ColourNumber == 10)
7592  {
7594  }
7595  else if(ColourNumber == 11)
7596  {
7598  }
7599  else if(ColourNumber == 12)
7600  {
7602  }
7603  else if(ColourNumber == 13)
7604  {
7606  }
7607  else if(ColourNumber == 14)
7608  {
7609  BackgroundColour = clTrainFailedBackground; // added at v2.4.0
7610 
7611  }
7612  // additional data
7614  // sets the BackgroundColour to the loaded value
7615  bool ForwardHeadCode = Utilities->LoadFileBool(InFile);
7616 
7617  if(ForwardHeadCode)
7618  {
7619  for(int x = 0; x < 4; x++)
7620  {
7622  }
7623  }
7624  else
7625  {
7626  for(int x = 0; x < 4; x++)
7627  {
7628  HeadCodePosition[x] = HeadCodeGrPtr[3 - x];
7629  }
7630  }
7631  // if crashed & in timetable mode then change FrontCodePtr to black, if in signaller mode then change to blue whether crashed or not
7632  if(TrainMode == Timetable)
7633  {
7634  if(Crashed)
7635  {
7637  }
7638  else
7639  {
7641  }
7642  }
7643  else
7644  {
7646  }
7648  // pick up background bitmaps, none if MidLag as no train plotted - entering at continuation
7649  if(Straddle == LeadMid)
7650  {
7651  if(LeadElement > -1)
7652  {
7654  }
7655  if(LeadElement > -1)
7656  {
7658  }
7659  if(MidElement > -1)
7660  {
7662  }
7663  if(MidElement > -1)
7664  {
7666  }
7667  }
7668  else if(Straddle == LeadMidLag)
7669  {
7670  if(LeadElement > -1)
7671  {
7673  }
7674  if(MidElement > -1)
7675  {
7677  }
7678  if(MidElement > -1)
7679  {
7681  }
7682  if(LagElement > -1)
7683  {
7685  }
7686  }
7687  int TrainDataEntryValue = Utilities->LoadFileInt(InFile);
7688 
7689  TrainDataEntryPtr = &(TrainController->TrainDataVector.at(0)) + TrainDataEntryValue;
7690  int ActionVectorEntryValue = Utilities->LoadFileInt(InFile);
7691 
7692  ActionVectorEntryPtr = &(TrainDataEntryPtr->ActionVector.at(0)) + ActionVectorEntryValue;
7693 
7694  // need to set the TrainID if arriving at a continuation but hasn't been plotted yet
7695  if(LeadElement > -1)
7696  // need to include this in case train exiting & no lead element
7697  {
7699  {
7700  Track->TrackElementAt(668, LeadElement).TrainIDOnElement = TrainID; // no need to stop gap flashing if a continuation
7701  }
7702  }
7703  AValue = sqrt(2 * PowerAtRail / Mass);
7704 
7705  AnsiString LocationAndMarker = Utilities->LoadFileString(InFile);
7706 
7707  // possible RestoreTimetableLocation + Marker, where Marker is
7708  // "*****0" for !StoppedWithoutPower and "*****1" otherwise (from v2.4.0)
7709  // Note: including RestoreTimetableLocation with the marker is to correct an oversight - RestoreTimetableLocation should have been saved earlier
7710  // added at beta v0.2e
7711  if((LocationAndMarker[1] != '*') && (LocationAndMarker.Length() > 6))
7712  // name not allowed to include the '*' character
7713  {
7714  AnsiString Location = LocationAndMarker.SubString(1, LocationAndMarker.Length() - 6);
7715  bool GiveMessagesFalse = false;
7716  bool CheckLocationsExistInRailwayTrue = true;
7717  if(TrainController->CheckLocationValidity(3, Location, GiveMessagesFalse, CheckLocationsExistInRailwayTrue))
7718  {
7719  // otherwise take no action
7720  RestoreTimetableLocation = Location;
7721  }
7722  }
7723  AnsiString Marker = LocationAndMarker.SubString(LocationAndMarker.Length() - 5, 6);
7724 
7725  StoppedWithoutPower = false;
7726  if(Marker[6] == '1')
7727  {
7728  StoppedWithoutPower = true;
7729  }
7730  Utilities->CallLogPop(1458);
7731 }
7732 
7733 // ---------------------------------------------------------------------------
7734 
7735 bool TTrain::CheckOneSessionTrain(std::ifstream &InFile)
7736 {
7738  {
7739  return(false); // HeadCode
7740 
7741  }
7742  if(!Utilities->CheckFileInt(InFile, 0, 1000000))
7743  {
7744  return(false); // RearStartElement
7745 
7746  }
7747  if(!Utilities->CheckFileInt(InFile, 0, 3))
7748  {
7749  return(false); // RearStartExitPos
7750 
7751  }
7752  if(!Utilities->CheckFileInt(InFile, 0, MaximumSpeedLimit))
7753  {
7754  return(false); // StartSpeed
7755 
7756  }
7757  if(!Utilities->CheckFileInt(InFile, 0, MaximumSpeedLimit))
7758  {
7759  return(false); // SignallerMaxSpeed
7760 
7761  }
7762  if(!Utilities->CheckFileBool(InFile))
7763  {
7764  return(false); // HoldAtLocationInTTMode
7765 
7766  }
7767  if(!Utilities->CheckFileInt(InFile, 0, 5760))
7768  {
7769  return(false); // RepeatNumber (max 96 x 60 at 1 min intervals)
7770 
7771  }
7772  if(!Utilities->CheckFileInt(InFile, 0, 5760))
7773  {
7774  return(false); // IncrementalMinutes (max 96 x 60)
7775 
7776  }
7777  if(!Utilities->CheckFileInt(InFile, 0, 1000000))
7778  {
7779  return(false); // IncrementalDigits
7780 
7781  }
7782  if(!Utilities->CheckFileInt(InFile, 0, 10000000))
7783  {
7784  return(false); // Mass
7785 
7786  }
7787  if(!Utilities->CheckFileInt(InFile, 0, 100000000))
7788  {
7789  return(false);
7790  }
7791  // FrontElementSpeedLimit - changed at v2.1.0 - effectively
7792  // not checked so as to allow for legacy session files, for new session files limit is set when loaded, see above
7793  if(!Utilities->CheckFileInt(InFile, 0, 10000000))
7794  {
7795  return(false); // FrontElementLength
7796 
7797  }
7798  if(!Utilities->CheckFileDouble(InFile))
7799  {
7800  return(false); // EntrySpeed
7801 
7802  }
7803  if(!Utilities->CheckFileDouble(InFile))
7804  {
7805  return(false); // ExitSpeedHalf
7806 
7807  }
7808  if(!Utilities->CheckFileDouble(InFile))
7809  {
7810  return(false); // ExitSpeedFull
7811 
7812  }
7813  if(!Utilities->CheckFileDouble(InFile))
7814  {
7815  return(false); // TimetableMaxRunningSpeed
7816 
7817  }
7818  if(!Utilities->CheckFileDouble(InFile))
7819  {
7820  return(false); // MaxRunningSpeed
7821 
7822  }
7823  if(!Utilities->CheckFileDouble(InFile))
7824  {
7825  return(false); // MaxExitSpeed
7826 
7827  }
7828  if(!Utilities->CheckFileDouble(InFile))
7829  {
7830  return(false); // MaxBrakeRate
7831 
7832  }
7833  if(!Utilities->CheckFileDouble(InFile))
7834  {
7835  return(false); // BrakeRate
7836 
7837  }
7838  if(!Utilities->CheckFileDouble(InFile))
7839  {
7840  return(false); // PowerAtRail
7841 
7842  }
7843  if(!Utilities->CheckFileBool(InFile))
7844  {
7845  return(false); // FirstHalfMove
7846 
7847  }
7848  if(!Utilities->CheckFileBool(InFile))
7849  {
7850  return(false); // OneLengthAccelDecel
7851 
7852  }
7853  if(!Utilities->CheckFileDouble(InFile))
7854  {
7855  return(false); // double(EntryTime)
7856 
7857  }
7858  if(!Utilities->CheckFileDouble(InFile))
7859  {
7860  return(false); // double(ExitTimeHalf)
7861 
7862  }
7863  if(!Utilities->CheckFileDouble(InFile))
7864  {
7865  return(false); // double(ExitTimeFull)
7866 
7867  }
7868  if(!Utilities->CheckFileDouble(InFile))
7869  {
7870  return(false); // double(ReleaseTime)
7871 
7872  }
7873  if(!Utilities->CheckFileDouble(InFile))
7874  {
7875  return(false); // double(TRSTime)
7876 
7877  }
7878  if(!Utilities->CheckFileDouble(InFile))
7879  {
7880  return(false); // double(LastActionTime)
7881 
7882  }
7883  if(!Utilities->CheckFileBool(InFile))
7884  {
7885  return(false); // CallingOnFlag
7886 
7887  }
7888  if(!Utilities->CheckFileBool(InFile))
7889  {
7890  return(false); // BeingCalledOn
7891 
7892  }
7893  if(!Utilities->CheckFileBool(InFile))
7894  {
7895  return(false); // DepartureTimeSet
7896 
7897  }
7898  if(!Utilities->CheckFileInt(InFile, 0, 2))
7899  {
7900  return(false); // (short)TrainMode
7901 
7902  }
7903  if(!Utilities->CheckFileBool(InFile))
7904  {
7905  return(false); // TimetableFinished
7906 
7907  }
7908  if(!Utilities->CheckFileBool(InFile))
7909  {
7910  return(false); // LastActionDelayFlag
7911 
7912  }
7913  if(!Utilities->CheckFileBool(InFile))
7914  {
7915  return(false); // SignallerRemoved
7916 
7917  }
7918  if(!Utilities->CheckFileBool(InFile))
7919  {
7920  return(false); // TerminatedMessageSent
7921 
7922  }
7923  if(!Utilities->CheckFileBool(InFile))
7924  {
7925  return(false); // Derailed
7926 
7927  }
7928  if(!Utilities->CheckFileBool(InFile))
7929  {
7930  return(false); // DerailPending
7931 
7932  }
7933  if(!Utilities->CheckFileBool(InFile))
7934  {
7935  return(false); // Crashed
7936 
7937  }
7938  if(!Utilities->CheckFileBool(InFile))
7939  {
7940  return(false); // StoppedAtBuffers
7941 
7942  }
7943  if(!Utilities->CheckFileBool(InFile))
7944  {
7945  return(false); // StoppedAtSignal
7946 
7947  }
7948  if(!Utilities->CheckFileBool(InFile))
7949  {
7950  return(false); // StoppedAtLocation
7951 
7952  }
7953  if(!Utilities->CheckFileBool(InFile))
7954  {
7955  return(false); // SignallerStopped
7956 
7957  }
7958  if(!Utilities->CheckFileBool(InFile))
7959  {
7960  return(false); // StoppedAfterSPAD
7961 
7962  }
7963  if(!Utilities->CheckFileBool(InFile))
7964  {
7965  return(false); // StoppedForTrainInFront
7966 
7967  }
7968  if(!Utilities->CheckFileBool(InFile))
7969  {
7970  return(false); // NotInService
7971 
7972  }
7973  if(!Utilities->CheckFileBool(InFile))
7974  {
7975  return(false); // Plotted
7976 
7977  }
7978  if(!Utilities->CheckFileBool(InFile))
7979  {
7980  return(false); // TrainGone
7981 
7982  }
7983  if(!Utilities->CheckFileBool(InFile))
7984  {
7985  return(false); // SPADFlag
7986 
7987  }
7988  if(!Utilities->CheckFileBool(InFile))
7989  {
7990  return(false); // TimeTimeLocArrived
7991 
7992  }
7993  if(!Utilities->CheckFileInt(InFile, 0, 15))
7994  {
7995  return(false); // HOffset[0]
7996 
7997  }
7998  if(!Utilities->CheckFileInt(InFile, 0, 15))
7999  {
8000  return(false); // HOffset[1]
8001 
8002  }
8003  if(!Utilities->CheckFileInt(InFile, 0, 15))
8004  {
8005  return(false); // HOffset[2]
8006 
8007  }
8008  if(!Utilities->CheckFileInt(InFile, 0, 15))
8009  {
8010  return(false); // HOffset[3]
8011 
8012  }
8013  if(!Utilities->CheckFileInt(InFile, 0, 15))
8014  {
8015  return(false); // VOffset[0]
8016 
8017  }
8018  if(!Utilities->CheckFileInt(InFile, 0, 15))
8019  {
8020  return(false); // VOffset[1]
8021 
8022  }
8023  if(!Utilities->CheckFileInt(InFile, 0, 15))
8024  {
8025  return(false); // VOffset[2]
8026 
8027  }
8028  if(!Utilities->CheckFileInt(InFile, 0, 15))
8029  {
8030  return(false); // VOffset[3]
8031 
8032  }
8033  if(!Utilities->CheckFileInt(InFile, -1, 1000000))
8034  {
8035  return(false); // PlotElement[0]
8036 
8037  }
8038  if(!Utilities->CheckFileInt(InFile, -1, 1000000))
8039  {
8040  return(false); // PlotElement[1]
8041 
8042  }
8043  if(!Utilities->CheckFileInt(InFile, -1, 1000000))
8044  {
8045  return(false); // PlotElement[2]
8046 
8047  }
8048  if(!Utilities->CheckFileInt(InFile, -1, 1000000))
8049  {
8050  return(false); // PlotElement[3]
8051 
8052  }
8053  if(!Utilities->CheckFileInt(InFile, 0, 3))
8054  {
8055  return(false); // PlotEntryPos[0]
8056 
8057  }
8058  if(!Utilities->CheckFileInt(InFile, 0, 3))
8059  {
8060  return(false); // PlotEntryPos[1]
8061 
8062  }
8063  if(!Utilities->CheckFileInt(InFile, 0, 3))
8064  {
8065  return(false); // PlotEntryPos[2]
8066 
8067  }
8068  if(!Utilities->CheckFileInt(InFile, 0, 3))
8069  {
8070  return(false); // PlotEntryPos[3]
8071 
8072  }
8073  if(!Utilities->CheckFileInt(InFile, -1, 1000000))
8074  {
8075  return(false); // TrainCrashedInto
8076 
8077  }
8078  if(!Utilities->CheckFileInt(InFile, 0, 2))
8079  {
8080  return(false); // (short)Straddle
8081 
8082  }
8083  if(!Utilities->CheckFileInt(InFile, 0, 1000000))
8084  {
8085  return(false); // NextTrainID
8086 
8087  }
8088  if(!Utilities->CheckFileInt(InFile, 0, 1000000))
8089  {
8090  return(false); // TrainID
8091 
8092  }
8093  if(!Utilities->CheckFileInt(InFile, -1, 1000000))
8094  {
8095  return(false); // LeadElement
8096 
8097  }
8098  if(!Utilities->CheckFileInt(InFile, 0, 3))
8099  {
8100  return(false); // LeadEntryPos
8101 
8102  }
8103  if(!Utilities->CheckFileInt(InFile, 0, 3))
8104  {
8105  return(false); // LeadExitPos
8106 
8107  }
8108  if(!Utilities->CheckFileInt(InFile, -1, 1000000))
8109  {
8110  return(false); // MidElement
8111 
8112  }
8113  if(!Utilities->CheckFileInt(InFile, 0, 3))
8114  {
8115  return(false); // MidEntryPos
8116 
8117  }
8118  if(!Utilities->CheckFileInt(InFile, 0, 3))
8119  {
8120  return(false); // MidExitPos
8121 
8122  }
8123  if(!Utilities->CheckFileInt(InFile, -1, 1000000))
8124  {
8125  return(false); // LagElement
8126 
8127  }
8128  if(!Utilities->CheckFileInt(InFile, 0, 3))
8129  {
8130  return(false); // LagEntryPos
8131 
8132  }
8133  if(!Utilities->CheckFileInt(InFile, 0, 3))
8134  {
8135  return(false); // LagExitPos
8136 
8137  }
8138  if(!Utilities->CheckFileInt(InFile, 0, 14))
8139  {
8140  return(false);
8141  }
8142  // Background colour number //14 is failed colour at v2.4.0
8143  if(!Utilities->CheckFileBool(InFile))
8144  {
8145  return(false); // ForwardHeadCode
8146 
8147  }
8148  if(!Utilities->CheckFileInt(InFile, 0, 10000))
8149  {
8150  return(false); // TrainDataEntryValue
8151 
8152  }
8153  if(!Utilities->CheckFileInt(InFile, 0, 10000))
8154  {
8155  return(false); // ActionVectorEntryValue
8156 
8157  }
8159  {
8160  return(false); // End of train marker + possible RestoreTimetableLocation
8161 
8162  }
8163  // and StoppedWithoutPower flag
8164  return(true);
8165 }
8166 
8167 // ---------------------------------------------------------------------------
8168 
8169 void TTrain::PlotTrainInZoomOutMode(int Caller, bool Flash)
8170 {
8171  // order below reflects significance so earlier shows first, as may have more than one flag set
8172  // only plot flashing trains when Flash is true
8173 
8174 /*
8175  clCrashedBackground (TColor)0x0000FF red
8176  clDerailedBackground (TColor)0x0000FF red
8177  clSPADBackground (TColor)0x00FFFF yellow
8178  clTrainFailedBackground (TColor)0x0066FF orange new at v2.4.0
8179  clCallOnBackground (TColor)0xFF33FF light magenta
8180  clSignalStopBackground (TColor)0x00FF66 green
8181  clBufferAttentionNeeded (TColor)0xFFFF00 cyan
8182  clStationStopBackground (TColor)0xCCFFCC pale green
8183  clTRSBackground (TColor)0xFFCCFF light pink
8184  clBufferStopBackground (TColor)0xFFFFCC pale cyan
8185  clStoppedTrainInFront (TColor)0xFF9999 lavender blue
8186  clSignallerStopped (TColor)0x99CCFF caramel
8187  clNormalBackground (TColor)0xCCCCCC grey
8188 */
8189 
8190  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",PlotTrainInZoomOutMode" + "," + HeadCode);
8191  bool HideFlashingTrain = true;
8192  // hide it when Flash false so it blinks on and off
8193  // if don't hide it it stays displayed all the time
8194  Graphics::TBitmap *SmallTrainBitmap;
8195 
8196  // NB ensure retain same order as zoomed in order so colours correspond
8198  {
8199  TrainController->CrashWarning = true;
8200  SmallTrainBitmap = RailGraphics->smRed;
8201  }
8203  {
8205  SmallTrainBitmap = RailGraphics->smRed;
8206  }
8208  {
8209  TrainController->SPADWarning = true;
8210  SmallTrainBitmap = RailGraphics->smYellow;
8211  }
8213  {
8215  SmallTrainBitmap = RailGraphics->smOrange;
8216  }
8218  {
8220  SmallTrainBitmap = RailGraphics->smMagenta;
8221  }
8223  {
8225  SmallTrainBitmap = RailGraphics->smBrightGreen;
8226  }
8228  {
8230  SmallTrainBitmap = RailGraphics->smCyan;
8231  }
8233  {
8234  SmallTrainBitmap = RailGraphics->smPaleGreen;
8235  HideFlashingTrain = false;
8236  }
8238  {
8239  SmallTrainBitmap = RailGraphics->smCyan;
8240  HideFlashingTrain = false;
8241  }
8243  {
8244  SmallTrainBitmap = RailGraphics->smLightBlue;
8245  HideFlashingTrain = false;
8246  }
8248  {
8249  SmallTrainBitmap = RailGraphics->smCaramel;
8250  HideFlashingTrain = false;
8251  }
8252  else
8253  {
8254  SmallTrainBitmap = RailGraphics->smBlack; // moving
8255  HideFlashingTrain = false;
8256  }
8257  // now plot the new train
8258  // just plot lead & mid, unless lead == -1 in which case plot mid & lag
8259  if((LeadElement > -1) && (!HideFlashingTrain || Flash))
8260  {
8261  Display->PlotSmallOutput(4, Track->TrackElementAt(441, LeadElement).HLoc * 4, Track->TrackElementAt(442, LeadElement).VLoc * 4, SmallTrainBitmap);
8262  }
8263  if((MidElement > -1) && (!HideFlashingTrain || Flash))
8264  {
8265  Display->PlotSmallOutput(5, Track->TrackElementAt(443, MidElement).HLoc * 4, Track->TrackElementAt(444, MidElement).VLoc * 4, SmallTrainBitmap);
8266  }
8267  if((LeadElement == -1) && (LagElement > -1) && (!HideFlashingTrain || Flash))
8268  {
8269  Display->PlotSmallOutput(6, Track->TrackElementAt(445, LagElement).HLoc * 4, Track->TrackElementAt(446, LagElement).VLoc * 4, SmallTrainBitmap);
8270  }
8274  Utilities->CallLogPop(1459);
8275 }
8276 
8277 // ---------------------------------------------------------------------------
8278 
8280 {
8281  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",UnplotTrainInZoomOutMode," + AnsiString(TrainID) + "," + HeadCode);
8282  if(!Display->ZoomOutFlag)
8283  {
8284  Utilities->CallLogPop(1304);
8285  return;
8286  }
8287  for(int y = 0; y < 3; y++)
8288  {
8289  if(OldZoomOutElement[y] > -1)
8290  {
8291  bool FoundFlag = false;
8292  TTrackElement ATElement = Track->TrackElementAt(717, OldZoomOutElement[y]);
8293  TTrackElement IATElement1, IATElement2;
8294  // default elements to begin with
8295  Display->PlotSmallOutput(7, ATElement.HLoc * 4, ATElement.VLoc * 4, RailGraphics->smSolidBgnd); // plot the blank
8296  TTrack::TIMPair IMPair = Track->GetVectorPositionsFromInactiveTrackMap(14, ATElement.HLoc, ATElement.VLoc, FoundFlag);
8297  // Note, have to plot inactives before track because track has to overwrite NamedLocationElements
8298  if(FoundFlag)
8299  {
8300  IATElement1 = Track->InactiveTrackElementAt(87, IMPair.first);
8301  Display->PlotSmallOutput(8, IATElement1.HLoc * 4, IATElement1.VLoc * 4, IATElement1.SmallGraphicPtr);
8302  if(IMPair.first != IMPair.second)
8303  {
8304  IATElement2 = Track->InactiveTrackElementAt(88, IMPair.second);
8305  Display->PlotSmallOutput(9, IATElement2.HLoc * 4, IATElement2.VLoc * 4, IATElement2.SmallGraphicPtr);
8306  }
8307  }
8308  Display->PlotSmallOutput(10, ATElement.HLoc * 4, ATElement.VLoc * 4, ATElement.SmallGraphicPtr);
8309  }
8310  }
8311  Utilities->CallLogPop(1305);
8312 }
8313 
8314 // ---------------------------------------------------------------------------
8315 
8316 bool TTrain::TrainAtLocation(int Caller, AnsiString &LocationName)
8317 {
8318  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",TrainAtLocation" + "," + HeadCode);
8319  LocationName = "";
8320  if(!StoppedAtLocation)
8321  {
8322  Utilities->CallLogPop(1398);
8323  return(false);
8324  }
8325  if(LeadElement > -1)
8326  {
8328  }
8329  if((LocationName == "") && (MidElement > -1))
8330  {
8331  LocationName = Track->TrackElementAt(682, MidElement).ActiveTrackElementName;
8332  }
8333  if((LocationName == "") && (LagElement > -1))
8334  {
8335  LocationName = Track->TrackElementAt(683, LagElement).ActiveTrackElementName;
8336  }
8337  if(LocationName == "")
8338  {
8339  throw Exception("Error - Location name not set in TrainAtLocation");
8340  }
8341  Utilities->CallLogPop(1399);
8342  return(true);
8343 }
8344 
8345 // ---------------------------------------------------------------------------
8346 
8347 void TTrain::PlotTrain(int Caller, TDisplay *Disp)
8348 {
8349  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",PlotTrain" + "," + HeadCode);
8350  for(int x = 0; x < 4; x++)
8351  {
8352  PlotTrainGraphic(7, x, Disp);
8353  }
8354  Utilities->CallLogPop(647);
8355 }
8356 
8357 // ---------------------------------------------------------------------------
8358 
8359 void TTrain::WriteTrainToImage(int Caller, Graphics::TBitmap *Bitmap)
8360 {
8361  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",WriteTrainToImage" + "," + HeadCode);
8362  for(int x = 0; x < 4; x++)
8363  {
8364  if(PlotElement[x] > -1)
8365  {
8366  Bitmap->Canvas->Draw(((Track->TrackElementAt(744, PlotElement[x]).HLoc - Track->GetHLocMin()) * 16 + HOffset[x]),
8367  ((Track->TrackElementAt(745, PlotElement[x]).VLoc - Track->GetVLocMin()) * 16 + VOffset[x]), HeadCodePosition[x]);
8368  }
8369  }
8370  Utilities->CallLogPop(1708);
8371 }
8372 
8373 // ---------------------------------------------------------------------------
8374 
8375 bool TTrain::LinkOccupied(int Caller, int TrackVectorPosition, int LinkNumber) // added at v1.2.0
8376 {
8377  // return true for any part of train occupying LinkNumber at TrackVectorPosition, false for anything else, including no LinkNumber & no TrackVectorPosition
8378  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",LinkOccupied," + AnsiString(TrackVectorPosition) + "," +
8379  AnsiString(LinkNumber) + "," + HeadCode);
8380 
8381 /* Note on Straddle: Straddle defines the actual train position wrt Lag, Mid & Lead elements at all times other than within UpdateTrain. Is only MidLag outside UpdateTrain
8382  on first entry at a continuation (with no train plotted), and that has no relevance here. In all other cases it is either LeadMid (when train fully
8383  on Lead & Mid elements) or LeadMidLag (when train straddling 3 elements).
8384 */
8385 
8386  // note that MidElement always fully occupied
8387  if((MidElement == TrackVectorPosition) && ((Track->TrackElementAt(883, TrackVectorPosition).Link[MidEntryPos] == LinkNumber) || (Track->TrackElementAt(884,
8388  TrackVectorPosition).Link[MidExitPos] == LinkNumber)))
8389  {
8390  Utilities->CallLogPop(2005);
8391  return(true);
8392  }
8393  if(Straddle == LeadMid)
8394  {
8395  if((LeadElement == TrackVectorPosition) && ((Track->TrackElementAt(885, TrackVectorPosition).Link[LeadEntryPos] == LinkNumber) ||
8396  (Track->TrackElementAt(886, TrackVectorPosition).Link[LeadExitPos] == LinkNumber)))
8397  {
8398  Utilities->CallLogPop(2006);
8399  return(true);
8400  }
8401  }
8402  else if(Straddle == LeadMidLag)
8403  {
8404  if((LeadElement == TrackVectorPosition) && (Track->TrackElementAt(887, TrackVectorPosition).Link[LeadEntryPos] == LinkNumber))
8405  // only interested in LeadEntryPos as train not occupying ExitPos yet
8406  {
8407  Utilities->CallLogPop(2007);
8408  return(true);
8409  }
8410  else if((LagElement == TrackVectorPosition) && (Track->TrackElementAt(888, TrackVectorPosition).Link[LagExitPos] == LinkNumber))
8411  // only interested in LagExitPos as train has left EntryPos
8412  {
8413  Utilities->CallLogPop(2008);
8414  return(true);
8415  }
8416  }
8417  Utilities->CallLogPop(2009);
8418  return(false);
8419 }
8420 
8421 // ---------------------------------------------------------------------------
8422 
8423 float TTrain::CalcTimeToAct(int Caller) // only called for running trains
8428 {
8429  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CalcTimeToAct, " + HeadCode);
8430  int DistanceToRedSignal = 0;
8431  float TimeToAct = 0;
8432  float MinsEarly = 0; //added at v2.6.1
8433  TDateTime DepartureTime; //added at v2.6.1 //ArrivalTime used instead of this at v2.9.0 but still calculate it in case need it later for some reason
8434  TDateTime ArrivalTime; //added at v2.9.0 as MinsEarly used DepartureTime which wasn't correct
8435 
8436  if(TrainFailed)
8437  {
8438  Utilities->CallLogPop(2147);
8439  return(0); // time to act now
8440  }
8441  if(SignallerStopped)
8442  {
8443  Utilities->CallLogPop(2080);
8444  return(-1);
8445  }
8446  if(BeingCalledOn) //added at v2.7.0 so zero time to act cancelled right away
8447  {
8448  Utilities->CallLogPop(2266);
8449  return(-1);
8450  }
8451  if(!Stopped() || StoppedAtLocation)
8452  {
8453  // calc distance to next red signal but check for continuation exit
8454  if(LeadElement == -1)
8455  // if -1 it's on an end element so no action needed
8456  {
8457  Utilities->CallLogPop(2075);
8458  return(-1);
8459  }
8460  else
8461  {
8462  int FirstPosToBeMeasured = Track->TrackElementAt(953, LeadElement).Conn[LeadExitPos];
8463  int FirstEntryPos = Track->TrackElementAt(954, LeadElement).ConnLinkPos[LeadExitPos];
8464  if((Straddle == LeadMidLag) && (TrainMode == Timetable))
8465 /* In TTMode it's important to set the first element to be measured ahead of the lead element only when the train fully on
8466  2 elements. Otherwise, if the train is only half on the lead element and approaching a station stop where the platform doesn't
8467  extend beyond the lead element stop point, the element ahead of the lead element is not a location whereas the ActionVector
8468  still points to the station stop location. In these circumstances the train hasn't yet stopped, so the dwell time at the
8469  stop isn't calculated, and the station to be stopped at isn't found as a future stop and nor are any other future stops
8470  because the ActionVector name never matches a future station. Hence all dwell times are omitted until the train lands fully
8471  on two elements. To avoid this when Straddle is LeadMidLag the first element to be measured is set to the lead element, so
8472  before the train has stopped the current station is still recognised as a future stop.
8473  In signaller mode stops don't count, and if pass red signal command is given then when have LeadMidLag the current element
8474  becomes the signal, and the time to act indication becomes 'NOW'.
8475 */
8476  {
8477  FirstPosToBeMeasured = LeadElement;
8478  FirstEntryPos = LeadEntryPos;
8479  }
8480  float CurrentStopTime; // set to 0 at start of function
8481  float LaterStopTime; // set to 0 at start of function
8482  float RecoverableTime; // set to 0 at start of function
8483  int AvTrackSpeed; // set to zero at start of function
8484  bool SigControlAndCanPassRedSignal = ((TrainMode == Signaller) && AllowedToPassRedSignal);
8485  DistanceToRedSignal = TrainController->CalcDistanceToRedSignalandStopTime(0, FirstPosToBeMeasured, FirstEntryPos, SigControlAndCanPassRedSignal,
8486  ActionVectorEntryPtr, HeadCode, TrainID, CurrentStopTime, LaterStopTime, RecoverableTime, AvTrackSpeed);
8487  if(DistanceToRedSignal == -1) // -1 for no action needed
8488  {
8489  Utilities->CallLogPop(2076);
8490  return(-1);
8491  }
8492 /* Have MinsDelayed; pos or neg,
8493  CurrentStopTime; pos or zero
8494  LaterStopTime; pos or zero
8495  RecoverableTime; pos or zero
8496 
8497  & from these calculate TotalStopTime. noting that:
8498  If stopped CurrentStopTime automatically adjusts for all early running and for as much late running as possible
8499  RecoverableTime always < LaterStopTime or both zero
8500  can't subtract more than RecoverableTime (MinsDelayed > 0)
8501  only subtract from LaterStopTime, not CurrentTime (MinsDelayed > 0)
8502  only subtract from LaterStopTime if LaterStopTime > 0 (MinsDelayed > 0)
8503  only add to LaterStopTime if LaterStopTime > 0 (MinsDelayed < 0)
8504  if running early & stopped at location CurrentStopTime will automatically include the excess
8505 */
8506  float TimeToSubtract, TotalStopTime;
8507  if(MinsDelayed > RecoverableTime)
8508  {
8509  TimeToSubtract = RecoverableTime;
8510  }
8511  else
8512  {
8513  TimeToSubtract = MinsDelayed; // may be negative;
8514 
8515  }
8516  if((AvTrackSpeed > 0) && (DistanceToStationStop <= DistanceToRedSignal) && (DistanceToStationStop > 0)) //protection against div by zero, not needed of no stop
8517  //before red signal, DistanceToStationStop != 0 as set to 0 if invalid
8518  //added at v2.6.1, DistanceToStationStop is calculated in SetTrainMovementValues, AvTrackSpeed is average to next red signal, but should be ok to use for
8519  //next station stop
8520  //after 2.7.0 changed (DistanceToStationStop < DistanceToRedSignal) to (DistanceToStationStop <= DistanceToRedSignal) because often have departure signal
8521  //next to the stop platform and if so the two distances are the same and the station stop time isn't included. Also after 2.7.0 used GetRepeatTime... to calc
8522  //departure time because ActionVectorEntryPtr->DepartureTime is the base time and therefore incorrect for repeats.
8523  //first find departure time from the next stop
8524  //at v2.9.0 changed MinsEarly calc to use ArrivalTime instead of departure time
8525  {
8526  if(ActionVectorEntryPtr->FormatType == TimeTimeLoc) //if already arrived then MinsEarly will be < 0 so becomes set to 0
8527  {
8530  MinsEarly = (double(ArrivalTime - TrainController->TTClockTime) * 86400 / 60) - (DistanceToStationStop * 3.6 / 60 / AvTrackSpeed);
8531  }
8532  else if((ActionVectorEntryPtr->FormatType == TimeLoc) && (ActionVectorEntryPtr->ArrivalTime != TDateTime(-1))) // not arrived yet
8533  {
8535  MinsEarly = (double(ArrivalTime - TrainController->TTClockTime) * 86400 / 60) - (DistanceToStationStop * 3.6 / 60 / AvTrackSpeed);
8536  if((ActionVectorEntryPtr + 1)->FormatType == TimeLoc)
8537  {
8538  // must be a departure
8539  DepartureTime = TrainController->GetRepeatTime(70, (ActionVectorEntryPtr + 1)->DepartureTime, RepeatNumber, IncrementalMinutes);
8540  }
8541  }
8542  else if((ActionVectorEntryPtr->FormatType == TimeLoc) && (ActionVectorEntryPtr->ArrivalTime == TDateTime(-1))) //already arrived
8543  {
8544  MinsEarly = 0;
8545  }
8546  if(MinsEarly < 0)
8547  {
8548  MinsEarly = 0;
8549  }
8550  }
8551  if(MinsDelayed < 0) // MinsDelayed < 0 means have arrived early at a station
8552  {
8553  if(CurrentStopTime > 0)
8554  {
8555  TotalStopTime = CurrentStopTime + LaterStopTime;
8556  }
8557  // stopped at loc, will depart on time
8558  else
8559  {
8560  TotalStopTime = LaterStopTime - MinsDelayed;
8561  }
8562  // not stopped, will depart on time at first later stop so add the delay
8563  }
8564  else if((MinsEarly > 0) && !Stopped()) //running early
8565  {
8566  TotalStopTime = LaterStopTime + MinsEarly;
8567  }
8568  else // on time or running late
8569  {
8570  if(LaterStopTime == 0)
8571  {
8572  TotalStopTime = CurrentStopTime;
8573  }
8574  // no later stops, if stopped now will depart as soon as possible,
8575  // if not stopped no stop times to add
8576  else
8577  {
8578  TotalStopTime = CurrentStopTime + LaterStopTime - TimeToSubtract; // later stops so deduct as much as can
8579  }
8580  }
8581  if(AvTrackSpeed < 30)
8582  {
8583  AvTrackSpeed = 30;
8584  }
8585  int Speed = AvTrackSpeed;
8586  if(AvTrackSpeed > int(MaxRunningSpeed))
8587  {
8588  Speed = int(MaxRunningSpeed);
8589  }
8590  if(TrainMode == Signaller)
8591  {
8592  Speed = SignallerMaxSpeed;
8593  TotalStopTime = 0;
8594  }
8595  TimeToAct = TotalStopTime + DistanceToRedSignal * 3.6 / 60 / Speed;
8596  // accel & decel taken into account in
8597  // CalcDistanceToRedSignalandStopTime
8598  // 3.6 convertsKm/h to m/s & 60 converts seconds to minutes
8599  Utilities->CallLogPop(2079);
8600  return(TimeToAct);
8601  }
8602  }
8603  else // stopped not at location
8604  {
8606  {
8607  TimeToAct = 0.0;
8608  }
8609  // but if stopped at a signal & autosigs route after it then ok
8610  if(StoppedAtSignal)
8611  {
8612  int NextElement = Track->TrackElementAt(928, LeadElement).Conn[LeadExitPos];
8613  int NextEntryPos = Track->TrackElementAt(929, LeadElement).ConnLinkPos[LeadExitPos];
8614  int NextExitPos;
8615  if(Track->TrackElementAt(930, NextElement).TrackType == Points)
8616  {
8617  if((NextEntryPos == 0) || (NextEntryPos == 2))
8618  // leading entry point
8619  {
8620  if(Track->TrackElementAt(931, NextElement).Attribute == 0)
8621  {
8622  NextExitPos = 1;
8623  }
8624  else
8625  {
8626  NextExitPos = 3;
8627  }
8628  }
8629  else
8630  {
8631  NextExitPos = 0; // trailing entry point
8632  }
8633  }
8634  else
8635  {
8636  NextExitPos = Track->GetNonPointsOppositeLinkPos(NextEntryPos);
8637  }
8638  int NextButOneElement = Track->TrackElementAt(932, NextElement).Conn[NextExitPos];
8639  int NextButOneEntryPos = Track->TrackElementAt(933, NextElement).ConnLinkPos[NextExitPos];
8640  int RouteNumber; // holder for referenced value, not used
8641  if(AllRoutes->GetRouteTypeAndNumber(32, NextButOneElement, NextButOneEntryPos, RouteNumber) == TAllRoutes::AutoSigsRoute)
8642  {
8643  TimeToAct = -1;
8644  }
8645  }
8646  Utilities->CallLogPop(2074);
8647  return(TimeToAct);
8648  }
8649 }
8650 
8651 // ---------------------------------------------------------------------------
8652 
8654 {
8655  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",TrainOnContinuation, " + HeadCode);
8656  if(LeadElement > -1)
8657  {
8659  {
8660  Utilities->CallLogPop(2148);
8661  return(true);
8662  }
8663  }
8664  if(MidElement > -1)
8665  {
8667  {
8668  Utilities->CallLogPop(2149);
8669  return(true);
8670  }
8671  }
8672  if(LagElement > -1)
8673  {
8675  {
8676  Utilities->CallLogPop(2150);
8677  return(true);
8678  }
8679  }
8680  Utilities->CallLogPop(2151);
8681  return(false);
8682 }
8683 
8684 // ---------------------------------------------------------------------------
8685 // TTrainController
8686 // ---------------------------------------------------------------------------
8687 
8689 {
8690  OnTimeArrivals = 0;
8691  LateArrivals = 0;
8692  EarlyArrivals = 0;
8693  OnTimePasses = 0;
8694  LatePasses = 0;
8695  EarlyPasses = 0;
8696  OnTimeExits = 0; //these 3 exits added at v2.9.2 - missed in error earlier
8697  LateExits = 0;
8698  EarlyExits = 0;
8699  OnTimeDeps = 0;
8700  LateDeps = 0;
8701  MissedStops = 0;
8702  OtherMissedEvents = 0;
8703  UnexpectedExits = 0;
8704  NumFailures = 0;
8705  IncorrectExits = 0;
8706  SPADEvents = 0;
8707  SPADRisks = 0;
8708  CrashedTrains = 0;
8709  Derailments = 0;
8710  TotArrDepPass = 0;
8711  TotLateArrMins = 0;
8712  TotEarlyArrMins = 0;
8713  TotLatePassMins = 0;
8714  TotEarlyPassMins = 0;
8715  TotLateExitMins = 0; //added at v2.9.1
8716  TotEarlyExitMins = 0; //added at v2.9.1
8717  TotLateDepMins = 0;
8718  ExcessLCDownMins = 0;
8719  TTClockTime = 0; // added for v0.6
8721  // added at v1.3.0 to ensure false at start
8722  OpTimeToActUpdateCounter = 0; // new v2.2.0
8723  OpActionPanelVisible = false; // new v2.2.0
8724  // reset all message flags, stops them being given twice (shouldn't be needed here but add for safety) //new at v2.4.0
8725  SSHigh = false;
8726  MRSHigh = false;
8727  MRSLow = false;
8728  MassHigh = false;
8729  BFHigh = false;
8730  BFLow = false;
8731  PwrHigh = false;
8732  SigSHigh = false;
8733  SigSLow = false;
8734  randomize();
8735  // to seed rand() & random() with a random number (see UpdateTrain)
8736 }
8737 
8738 // ---------------------------------------------------------------------------
8739 
8741 {
8742  for(unsigned int x = 0; x < TrainVector.size(); x++)
8743  {
8744  TrainVectorAt(32, x).DeleteTrain(4);
8745  }
8746  TrainVector.clear();
8747 }
8748 
8749 // ---------------------------------------------------------------------------
8750 
8751 void TTrainController::LogEvent(AnsiString Str)
8752 {
8753  AnsiString FullStr = Utilities->TimeStamp() + "," + TTClockTime.FormatString("hh:nn:ss") + "," + Str;
8754 
8755  // restrict to last 1000 entries
8756  Utilities->EventLog.push_back(FullStr);
8757  if(Utilities->EventLog.size() > 1000)
8758  {
8759  Utilities->EventLog.pop_front();
8760  }
8761 }
8762 
8763 // ---------------------------------------------------------------------------
8764 
8766 {
8767  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",Operate");
8768  bool ClockState = Utilities->Clock2Stopped;
8769 
8770  Utilities->Clock2Stopped = true;
8771  // new section dealing with Snt & Snt-sh additions
8772  // BUT don't add trains if points or route flashing [conditions added for Version 0.6 as a result of Najamuddin's error - 15/01/11] - wait until next
8773  // clock tick after stops flashing
8775  {
8776  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
8777  {
8778  TTrainDataEntry & TDEntry = TrainDataVector.at(x);
8779  const TActionVectorEntry &AVEntry0 = TDEntry.ActionVector.at(0);
8780  TActionEventType EventType = NoEvent;
8781  if(AVEntry0.Command == "Snt")
8782  {
8783  // calc below only for Snt & Snt-sh entries rather than all entries to save time
8784  const TActionVectorEntry &AVEntryLast = TDEntry.ActionVector.at(TDEntry.ActionVector.size() - 1);
8785  int IncrementalMinutes = 0;
8786  int IncrementalDigits = 0;
8787  if(AVEntryLast.FormatType == Repeat)
8788  {
8789  IncrementalMinutes = AVEntryLast.RearStartOrRepeatMins;
8790  IncrementalDigits = AVEntryLast.FrontStartOrRepeatDigits;
8791  }
8792  if((AVEntryLast.FormatType == Repeat) && (TDEntry.NumberOfTrains < 2))
8793  {
8794  throw Exception("Error - Repeat entry && less than two trains for Snt entry: " + TDEntry.HeadCode);
8795  }
8796  // see above note
8797 
8798  for(int y = 0; y < TDEntry.NumberOfTrains; y++)
8799  {
8800  TTrainOperatingData &TTOD = TDEntry.TrainOperatingDataVector.at(y);
8801  if(TTOD.RunningEntry != NotStarted)
8802  {
8803  continue;
8804  }
8805  if(GetRepeatTime(2, AVEntry0.EventTime, y, IncrementalMinutes) > TTClockTime)
8806  {
8807  break; // all the rest will also be greater
8808  }
8809  AnsiString TrainHeadCode = GetRepeatHeadCode(22, TDEntry.HeadCode, y, IncrementalDigits);
8810  if(AddTrain(2, AVEntry0.RearStartOrRepeatMins, AVEntry0.FrontStartOrRepeatDigits, TrainHeadCode, TDEntry.StartSpeed, TDEntry.Mass,
8811  TDEntry.MaxRunningSpeed, TDEntry.MaxBrakeRate, TDEntry.PowerAtRail, "Timetable", &TDEntry, y, IncrementalMinutes, IncrementalDigits,
8812  TDEntry.SignallerSpeed, AVEntry0.SignallerControl, EventType))
8813  {
8814  TTOD.TrainID = TrainVector.back().TrainID;
8815  TTOD.RunningEntry = Running;
8816  }
8817  else if(EventType == FailTrainEntry)
8818  {
8819  break; // if a train can't enter no point checking any more repeats as they won't be able to enter either
8820  }
8821  }
8822  }
8823  if(AVEntry0.Command == "Snt-sh")
8824  // just start this once, shuttle repeats take care of restarts
8825  {
8826  // calc below only for Snt & Snt-sh entries rather than all entries to save time
8827  const TActionVectorEntry &AVEntryLast = TDEntry.ActionVector.at(TDEntry.ActionVector.size() - 1);
8828  int IncrementalMinutes = 0;
8829  int IncrementalDigits = 0;
8830  if(AVEntryLast.FormatType == Repeat)
8831  {
8832  IncrementalMinutes = AVEntryLast.RearStartOrRepeatMins;
8833  IncrementalDigits = AVEntryLast.FrontStartOrRepeatDigits;
8834  }
8835  if((AVEntryLast.FormatType == Repeat) && (TDEntry.NumberOfTrains < 2))
8836  {
8837  throw Exception("Error - Repeat entry && less than two trains for Snt-sh entry: " + TDEntry.HeadCode);
8838  }
8839  // see above note
8840  TTrainOperatingData &TTOD = TDEntry.TrainOperatingDataVector.at(0);
8841  if(TTOD.RunningEntry == NotStarted)
8842  {
8843  if(AVEntry0.EventTime <= TTClockTime)
8844  {
8845  if(AddTrain(3, AVEntry0.RearStartOrRepeatMins, AVEntry0.FrontStartOrRepeatDigits, TDEntry.HeadCode, TDEntry.StartSpeed, TDEntry.Mass,
8846  TDEntry.MaxRunningSpeed, TDEntry.MaxBrakeRate, TDEntry.PowerAtRail, "Timetable", &TDEntry, 0, IncrementalMinutes, IncrementalDigits,
8847  TDEntry.SignallerSpeed, false, EventType))
8848  // false for SignallerControl
8849  {
8850  TTOD.TrainID = TrainVector.back().TrainID;
8851  TTOD.RunningEntry = Running;
8852  }
8853  else if(EventType == FailTrainEntry)
8854  {
8855  break; // if a train can't enter no point checking any more repeats as they won't be able to enter either
8856  }
8857  }
8858  }
8859  }
8860  }
8861  }
8862  // deal with running trains but abort if any vectors added, would probably be OK but don't risk a vector reallocation disrupting the
8863  // iteration, next cycle will catch up with any other pending updates
8864  if(!TrainVector.empty())
8865  {
8866  TrainAdded = false;
8867  AllRoutes->CallonVector.clear();
8868  // this will be rebuilt during the calls to UpdateTrain
8869  for(unsigned int x = 0; x < TrainVector.size(); x++)
8870  {
8871  TrainVectorAt(33, x).UpdateTrain(0);
8872  if(TrainAdded || TrainVectorAt(35, x).HasTrainGone())
8873  // added HasTrainGone() condition in v0.4c to prevent 2 trains both having TrainGone set
8874  // at the same time. That caused the error Craig Weekes reported in November 2010 where 2 trains exited at the same time, and later the TrainVector
8875  // iterates in reverse to erase the second train to have gone, but afterwards ReplotTrains iterates forwards and therefore replots the first train to
8876  // have gone and therefore sets the TrainIDOnElement value to the exited train, with nothing to reset it. Hovering the mouse over that element with
8877  // train information enabled causes an error because the track element thinks the train is still there, whereas it is missing from the TrainVector.
8878  {
8879  break;
8880  }
8881  }
8882  // set warning flags
8883  CrashWarning = false;
8884  DerailWarning = false;
8885  SPADWarning = false;
8886  CallOnWarning = false;
8887  SignalStopWarning = false;
8888  BufferAttentionWarning = false;
8889  TrainFailedWarning = false;
8890  for(int x = TrainVector.size() - 1; x >= 0; x--) // reverse because of erase
8891  {
8892  TTrain &Train = TrainVectorAt(34, x);
8893  if(Train.Crashed)
8894  // can't use background colours for crashed & derailed because same colour
8895  {
8896  CrashWarning = true;
8897  }
8898  else if(Train.Derailed)
8899  // can't use background colours for crashed & derailed because same colour
8900  {
8901  DerailWarning = true;
8902  }
8903  else if(Train.BackgroundColour == clSPADBackground)
8904  // use colour as that changes as soon as passes signal
8905  {
8906  SPADWarning = true;
8907  }
8908  else if(Train.BackgroundColour == clTrainFailedBackground)
8909  {
8910  TrainFailedWarning = true;
8911  }
8912  else if(Train.BackgroundColour == clCallOnBackground)
8913  // use colour as also stopped at signal
8914  {
8915  CallOnWarning = true;
8916  }
8917  else if(Train.BackgroundColour == clSignalStopBackground)
8918  // use colour to distinguish from call-on
8919  {
8920  SignalStopWarning = true;
8921  }
8922  else if(Train.BackgroundColour == clBufferAttentionNeeded)
8923  // use colour to distinguish from ordinary buffer stop
8924  {
8925  BufferAttentionWarning = true;
8926  }
8927  if(Train.HasTrainGone())
8928  {
8929  AnsiString Loc = "";
8930  bool ElementFound = false;
8931  TTrackElement TE;
8932  if(Train.LagElement > -1)
8933  {
8934  TE = Track->TrackElementAt(531, Train.LagElement);
8935  ElementFound = true;
8936  }
8937  else if(Train.MidElement > -1)
8938  {
8939  TE = Track->TrackElementAt(779, Train.MidElement);
8940  ElementFound = true;
8941  }
8942  else if(Train.LeadElement > -1)
8943  {
8944  TE = Track->TrackElementAt(780, Train.LeadElement);
8945  ElementFound = true;
8946  }
8947  if(ElementFound)
8948  {
8949  if(TE.ActiveTrackElementName != "")
8950  {
8951  Loc = TE.ActiveTrackElementName + ", track element " + TE.ElementID;
8952  }
8953  else
8954  {
8955  Loc = "track element " + TE.ElementID;
8956  }
8957  }
8958  TActionVectorEntry *AVEntryPtr = Train.ActionVectorEntryPtr;
8959  if((Train.SignallerRemoved) || (Train.JoinedOtherTrainFlag))
8960  // need above first because may also have ActionVectorEntryPtr == "Fer"
8961  {
8962  Train.UnplotTrain(9);
8963  // added at v1.3.0 to reset signals after train removed from an autosigsroute
8965  {
8968  }
8969  // end of addition
8970  AllRoutes->RebuildRailwayFlag = true;
8971  // to force ClearandRebuildRailway at next clock tick if not in zoom-out mode, to replot LCs
8972  // correctly after a crash
8973  }
8974  else if(AVEntryPtr->Command == "Fer")
8975  {
8976  bool CorrectExit = false;
8977  if(!AVEntryPtr->ExitList.empty())
8978  {
8979  for(TNumListIterator ELIT = AVEntryPtr->ExitList.begin(); ELIT != AVEntryPtr->ExitList.end(); ELIT++)
8980  {
8981  if(*ELIT == Train.LagElement)
8982  {
8983  CorrectExit = true;
8984  }
8985  }
8986  }
8987  if(CorrectExit)
8988  {
8989  Train.LogAction(19, Train.HeadCode, "", Leave, Loc, AVEntryPtr->EventTime, AVEntryPtr->Warning);
8990  }
8991  else
8992  {
8993  LogActionError(38, Train.HeadCode, "", FailIncorrectExit, Loc);
8994  }
8995  }
8996  else
8997  {
8998  if(!AVEntryPtr->SignallerControl)
8999  {
9000  LogActionError(26, Train.HeadCode, "", FailUnexpectedExitRailway, Loc);
9001  Train.SendMissedActionLogs(2, -2, AVEntryPtr);
9002  // -2 is marker for send messages for all remaining actions except Fer if present
9003  }
9004  else
9005  {
9006  Train.LogAction(31, Train.HeadCode, "", SignallerLeave, Loc, TDateTime(0), false); // false for Warning
9007  }
9008  }
9009  Train.TrainDataEntryPtr->TrainOperatingDataVector.at(Train.RepeatNumber).RunningEntry = Exited;
9010  Train.DeleteTrain(1);
9011  TrainVector.erase(TrainVector.begin() + x);
9012  ReplotTrains(1, Display);
9013  // to reset ElementIDs for remaining trains when have removed a train
9014  }
9015  }
9016  }
9017  else
9018  {
9019  // reset all flags in case last train removed with flag set
9020  CrashWarning = false;
9021  DerailWarning = false;
9022  SPADWarning = false;
9023  CallOnWarning = false;
9024  SignalStopWarning = false;
9025  BufferAttentionWarning = false;
9026  TrainFailedWarning = false;
9027  }
9028  // update OpTimeToActMultimap
9030  {
9032  // clears entries then adds values for running trains then for continuation entries
9033  }
9034  Utilities->Clock2Stopped = ClockState;
9035  Utilities->CallLogPop(723);
9036 }
9037 
9038 // ---------------------------------------------------------------------------
9040 {
9041  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",FinishedOperation");
9042  if(!TrainVector.empty())
9043  {
9044  for(int x = TrainVector.size() - 1; x >= 0; x--)
9045  {
9046  TrainVectorAt(50, x).DeleteTrain(2);
9047  }
9048  TrainVector.clear();
9049  }
9050  if(!TrainDataVector.empty())
9051  {
9052  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
9053  {
9054  TTrainDataEntry &TDEntry = TrainDataVector.at(x);
9055  for(int y = 0; y < TDEntry.NumberOfTrains; y++)
9056  {
9057  TTrainOperatingData &TOD = TDEntry.TrainOperatingDataVector.at(y);
9058  TOD.RunningEntry = NotStarted;
9059  TOD.TrainID = -1;
9060  TOD.EventReported = NoEvent;
9061  }
9062  }
9063  }
9064  Display->GetOutputLog1()->Caption = "";
9065  Display->GetOutputLog2()->Caption = "";
9066  Display->GetOutputLog3()->Caption = "";
9067  Display->GetOutputLog4()->Caption = "";
9068  Display->GetOutputLog5()->Caption = "";
9069  Display->GetOutputLog6()->Caption = "";
9070  Display->GetOutputLog7()->Caption = "";
9071  Display->GetOutputLog8()->Caption = "";
9072  Display->GetOutputLog9()->Caption = "";
9073  Display->GetOutputLog10()->Caption = "";
9074  Utilities->CallLogPop(1352);
9075 }
9076 
9077 // ---------------------------------------------------------------------------
9078 
9080 {
9081  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",ReplotTrains");
9082  if(!TrainVector.empty())
9083  {
9084  for(unsigned int x = 0; x < TrainVector.size(); x++)
9085  {
9086  TrainVectorAt(51, x).PlotTrain(4, Disp);
9087  }
9088  }
9089  Utilities->CallLogPop(724);
9090 }
9091 
9092 // ---------------------------------------------------------------------------
9093 
9094 void TTrainController::WriteTrainsToImage(int Caller, Graphics::TBitmap *Bitmap)
9095 {
9096  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",WriteTrainsToImage");
9097  if(!TrainVector.empty())
9098  {
9099  for(unsigned int x = 0; x < TrainVector.size(); x++)
9100  {
9101  TrainVectorAt(61, x).WriteTrainToImage(0, Bitmap);
9102  }
9103  }
9104  Utilities->CallLogPop(1707);
9105 }
9106 
9107 // ---------------------------------------------------------------------------
9108 
9110 {
9111  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",UnplotTrains");
9112  if(!TrainVector.empty())
9113  {
9114  for(unsigned int x = 0; x < TrainVector.size(); x++)
9115  {
9116  TrainVectorAt(52, x).UnplotTrain(10);
9117  }
9118  }
9120  Utilities->CallLogPop(725);
9121 }
9122 
9123 // ---------------------------------------------------------------------------
9124 
9125 bool TTrainController::AddTrain(int Caller, int RearPosition, int FrontPosition, AnsiString HeadCode, int StartSpeed, int Mass, double MaxRunningSpeed,
9126  double MaxBrakeRate, double PowerAtRail, AnsiString ModeStr, TTrainDataEntry *TrainDataEntryPtr, int RepeatNumber, int IncrementalMinutes,
9127  int IncrementalDigits, int SignallerSpeed, bool SignallerControl, TActionEventType &EventType)
9128 {
9129  LogEvent(AnsiString(Caller) + ",AddTrain," + AnsiString(RearPosition) + "," + AnsiString(FrontPosition) + "," + HeadCode + "," + AnsiString(StartSpeed) +
9130  "," + AnsiString(Mass) + "," + ModeStr);
9131  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",AddTrain," + AnsiString(RearPosition) + "," + AnsiString(FrontPosition) +
9132  "," + HeadCode + "," + AnsiString(StartSpeed) + "," + AnsiString(Mass) + "," + ModeStr + "," + HeadCode);
9133 
9134  int RearExitPos = -1;
9135 
9136  for(int x = 0; x < 4; x++)
9137  {
9138  if(Track->TrackElementAt(519, RearPosition).Conn[x] == FrontPosition)
9139  {
9140  RearExitPos = x;
9141  }
9142  }
9143  if(RearExitPos == -1)
9144  {
9145  throw Exception("Error, RearExit == -1 in AddTrain");
9146  }
9147  bool ReportFlag = true;
9148 
9149  // used to stop repeated messages from CheckStartAllowable when split failed
9150  if(TrainDataEntryPtr->TrainOperatingDataVector.at(RepeatNumber).EventReported != NoEvent)
9151  {
9152  ReportFlag = false;
9153  }
9154  if(!CheckStartAllowable(0, RearPosition, RearExitPos, HeadCode, ReportFlag, EventType))
9155  {
9156  // messages sent to performance log in CheckStartAllowable if ReportFlag true
9157  TrainDataEntryPtr->TrainOperatingDataVector.at(RepeatNumber).EventReported = EventType;
9158  Utilities->CallLogPop(938);
9159  return(false);
9160  }
9161  TrainDataEntryPtr->TrainOperatingDataVector.at(RepeatNumber).EventReported = NoEvent;
9162  TTrainMode TrainMode = NoMode;
9163 
9164  if(ModeStr == "Timetable")
9165  {
9166  TrainMode = Timetable;
9167  }
9168  // all else gives 'None', 'Signaller' set within program
9169 
9170  if(MaxRunningSpeed < 10)
9171  {
9172  MaxRunningSpeed = 10; // added at v0.6 to avoid low max speeds
9173  }
9174  if(SignallerSpeed < 10)
9175  {
9176  SignallerSpeed = 10; // added at v0.6 to avoid low max speeds
9177  }
9178  TTrain *NewTrain = new TTrain(0, RearPosition, RearExitPos, HeadCode, StartSpeed, Mass, MaxRunningSpeed, MaxBrakeRate, PowerAtRail, TrainMode,
9179  TrainDataEntryPtr, RepeatNumber, IncrementalMinutes, IncrementalDigits, SignallerSpeed);
9180 
9181  NewTrain->ActionVectorEntryPtr = &(TrainDataEntryPtr->ActionVector.at(0));
9182  // initialise here rather than in TTrain constructor as create trains
9183  // with Null TrainDataEntryPtr when loading session trains
9184  if(SignallerControl)
9185  {
9186  NewTrain->TimetableFinished = true;
9187  NewTrain->SignallerStoppingFlag = false;
9188  NewTrain->TrainMode = Signaller;
9189  if(NewTrain->MaxRunningSpeed > NewTrain->SignallerMaxSpeed)
9190  {
9191  NewTrain->MaxRunningSpeed = NewTrain->SignallerMaxSpeed;
9192  }
9194  }
9195  // deal with starting conditions:-
9196  // unlocated Snt: just report entry & advance pointer
9197  // located Snt or Sfs: set station conditions as would if had reached stop point in Update(), & advance the ActionVectorEntryPtr
9198  // Sns doesn't need a new train
9199  if(NewTrain->ActionVectorEntryPtr->LocationName != "")
9200  // covers all above located starts
9201  // if location of Snt was a station (that is set as LocationName, i.e. not just any station) that isn't next departure station then
9202  // wouldn't have accepted the timetable
9203  {
9204  // first check if LeadElement (can't access LeadElement directly yet as not set, use FrontPosition instead) is buffers, note that
9205  // StoppedAtBuffers is set in UpdateTrain()
9206  if(Track->TrackElementAt(520, FrontPosition).TrackType == Buffers)
9207  // buffer end must be ahead of train or would have failed start position check
9208  {
9209  NewTrain->StoppedAtLocation = true;
9210  NewTrain->PlotStartPosition(0);
9212  NewTrain->LogAction(20, NewTrain->HeadCode, "", Create, NewTrain->ActionVectorEntryPtr->LocationName, NewTrain->ActionVectorEntryPtr->EventTime,
9213  NewTrain->ActionVectorEntryPtr->Warning);
9214  if(!SignallerControl) // don't advance if SignalControlEntry
9215  {
9216  NewTrain->ActionVectorEntryPtr++;
9217  // should be a command, could be a location departure but if so can't depart so set 'Hold' anyway
9218  }
9219  NewTrain->LastActionTime = TTClockTime;
9220  }
9221  // else a through station stop
9222  else
9223  {
9224  NewTrain->StoppedAtLocation = true;
9225  NewTrain->PlotStartPosition(10);
9227  NewTrain->LogAction(21, NewTrain->HeadCode, "", Create, NewTrain->ActionVectorEntryPtr->LocationName, NewTrain->ActionVectorEntryPtr->EventTime,
9228  NewTrain->ActionVectorEntryPtr->Warning);
9229  if(!SignallerControl) // don't advance if SignalControlEntry
9230  {
9231  NewTrain->ActionVectorEntryPtr++;
9232  }
9233  NewTrain->LastActionTime = TTClockTime;
9234  }
9235  }
9236  else // unlocated entry (i.e. not a stop entry, but could still be at a named location)
9237  {
9238  NewTrain->PlotStartPosition(11);
9239  TTrackElement TE = Track->TrackElementAt(530, NewTrain->RearStartElement);
9240  AnsiString Loc = "";
9241  if(TE.ActiveTrackElementName != "")
9242  {
9243  Loc = TE.ActiveTrackElementName + ", track element " + TE.ElementID;
9244  }
9245  else
9246  {
9247  Loc = "track element " + TE.ElementID;
9248  }
9249  if(TE.TrackType == Continuation)
9250  {
9251  NewTrain->LogAction(22, NewTrain->HeadCode, "", Enter, Loc, NewTrain->ActionVectorEntryPtr->EventTime, NewTrain->ActionVectorEntryPtr->Warning);
9252  }
9253  else
9254  {
9255  NewTrain->LogAction(23, NewTrain->HeadCode, "", Create, Loc, NewTrain->ActionVectorEntryPtr->EventTime, NewTrain->ActionVectorEntryPtr->Warning);
9256  }
9257  if(!SignallerControl) // don't advance if SignalControlEntry
9258  {
9259  NewTrain->ActionVectorEntryPtr++;
9260  }
9261  NewTrain->LastActionTime = TTClockTime;
9262  // no need to set LastActionTime for an unlocated entry
9263  }
9264  // cancel a wrong-direction route if either element of train starts on one
9265  if(NewTrain->LeadElement > -1)
9266  {
9267  NewTrain->CheckAndCancelRouteForWrongEndEntry(3, NewTrain->LeadElement, NewTrain->LeadEntryPos);
9268  }
9269  if(NewTrain->MidElement > -1)
9270  {
9271  NewTrain->CheckAndCancelRouteForWrongEndEntry(4, NewTrain->MidElement, NewTrain->MidEntryPos);
9272  }
9273  // set signals for a right-direction autosigs route for either element of train on one
9274  // erase elements back to start for a non-autosigs route & check if an autosigs route immediately behind it, and if so set its signals
9275  // note that all but autosigs routes become part of a single route, so there can only be an autosigs route behind the non-autosigs route
9276  int RouteNumber = -1;
9277  bool SignalsSet = false;
9278 
9279  if(NewTrain->LeadElement > -1)
9280  {
9281  if(AllRoutes->GetRouteTypeAndNumber(13, NewTrain->LeadElement, NewTrain->LeadEntryPos, RouteNumber) == TAllRoutes::AutoSigsRoute)
9282  {
9283  // below added in place of SetRouteSignals in v2.4.0 as don't want to set signals from start of route for a new train addition
9284  int RouteStartPosition;
9285  TAllRoutes::TRouteElementPair FirstPair, SecondPair;
9286  FirstPair = AllRoutes->GetRouteElementDataFromRoute2MultiMap(21, Track->TrackElementAt(955, FrontPosition).HLoc,
9287  Track->TrackElementAt(956, FrontPosition).VLoc, SecondPair);
9288  if(FirstPair.first == RouteNumber)
9289  {
9290  RouteStartPosition = FirstPair.second;
9291  }
9292  else if(SecondPair.first == RouteNumber)
9293  {
9294  RouteStartPosition = SecondPair.second;
9295  }
9296  else
9297  {
9298  throw Exception("Error, RouteNumber not found in Route2MultiMap in 1st of 2 calls to SetAllRearwardsSignals in AddTrain");
9299  }
9300  AllRoutes->SetAllRearwardsSignals(10, 0, RouteNumber, RouteStartPosition);
9301  SignalsSet = true;
9302  // AllRoutes->GetFixedRouteAt(, RouteNumber).SetRouteSignals(); above substituted in v2.4.0
9303  }
9304  else if(RouteNumber > -1) // non-autosigsroute
9305  {
9306  TPrefDirElement TempPDE = AllRoutes->GetFixedRouteAt(181, RouteNumber).GetFixedPrefDirElementAt(194, 0);
9307  int FirstTVPos = TempPDE.GetTrackVectorPosition();
9308  int FirstELinkPos = TempPDE.GetELinkPos();
9309  while(TempPDE.GetTrackVectorPosition() != (unsigned int)(NewTrain->LeadElement))
9310  {
9311  AllRoutes->RemoveRouteElement(16, TempPDE.HLoc, TempPDE.VLoc, TempPDE.GetELink());
9312  TempPDE = AllRoutes->GetFixedRouteAt(182, RouteNumber).GetFixedPrefDirElementAt(195, 0);
9313  }
9314  if(TempPDE.GetTrackVectorPosition() == (unsigned int)(NewTrain->LeadElement))
9315  {
9316  AllRoutes->RemoveRouteElement(17, TempPDE.HLoc, TempPDE.VLoc, TempPDE.GetELink());
9317  // remove the last element under LeadElement
9318  }
9319  AllRoutes->RebuildRailwayFlag = true;
9320  // to force ClearandRebuildRailway at next clock tick if not in zoom-out mode
9321  // now deal with a rear linked autosigs route
9322  if(Track->TrackElementAt(820, FirstTVPos).Conn[FirstELinkPos] > -1)
9323  {
9324  int LinkedRouteNumber = -1;
9325  if(AllRoutes->GetRouteTypeAndNumber(17, Track->TrackElementAt(821, FirstTVPos).Conn[FirstELinkPos],
9326  Track->TrackElementAt(822, FirstTVPos).ConnLinkPos[FirstELinkPos], LinkedRouteNumber) == TAllRoutes::AutoSigsRoute)
9327  {
9328  AllRoutes->GetFixedRouteAt(169, LinkedRouteNumber).SetRouteSignals(0);
9329  // this is ok as here we are setting signals from the start of the route
9330  }
9331  }
9332  SignalsSet = true;
9333  }
9334  }
9335  if(NewTrain->MidElement > -1)
9336  // if entering at a continuation MidElement == -1
9337  {
9338  // this is included in case a train starts with LeadElement on no route and MidElement on a route
9339  if(!SignalsSet)
9340  {
9341  RouteNumber = -1;
9342  if(AllRoutes->GetRouteTypeAndNumber(14, NewTrain->MidElement, NewTrain->MidEntryPos, RouteNumber) == TAllRoutes::AutoSigsRoute)
9343  {
9344  // below added in place of SetRouteSignals in v2.4.0 as don't want to set signals from start of route for a new train addition
9345  int RouteStartPosition;
9346  TAllRoutes::TRouteElementPair FirstPair, SecondPair;
9347  FirstPair = AllRoutes->GetRouteElementDataFromRoute2MultiMap(22, Track->TrackElementAt(957, RearPosition).HLoc,
9348  Track->TrackElementAt(958, RearPosition).VLoc, SecondPair);
9349  if(FirstPair.first == RouteNumber)
9350  {
9351  RouteStartPosition = FirstPair.second;
9352  }
9353  else if(SecondPair.first == RouteNumber)
9354  {
9355  RouteStartPosition = SecondPair.second;
9356  }
9357  else
9358  {
9359  throw Exception("Error, RouteNumber not found in Route2MultiMap in 2nd of 2 calls to SetAllRearwardsSignals in AddTrain");
9360  }
9361  AllRoutes->SetAllRearwardsSignals(11, 0, RouteNumber, RouteStartPosition);
9362  SignalsSet = true;
9363  // AllRoutes->GetFixedRouteAt(, RouteNumber).SetRouteSignals(); above substituted in v2.4.0
9364  }
9365  else if(RouteNumber > -1) // non-autosigsroute
9366  {
9367  TPrefDirElement TempPDE = AllRoutes->GetFixedRouteAt(184, RouteNumber).GetFixedPrefDirElementAt(196, 0);
9368  int FirstTVPos = TempPDE.GetTrackVectorPosition();
9369  int FirstELinkPos = TempPDE.GetELinkPos();
9370  while(TempPDE.GetTrackVectorPosition() != (unsigned int)(NewTrain->MidElement))
9371  {
9372  AllRoutes->RemoveRouteElement(18, TempPDE.HLoc, TempPDE.VLoc, TempPDE.GetELink());
9373  TempPDE = AllRoutes->GetFixedRouteAt(185, RouteNumber).GetFixedPrefDirElementAt(197, 0);
9374  }
9375  if(TempPDE.GetTrackVectorPosition() == (unsigned int)(NewTrain->MidElement))
9376  {
9377  AllRoutes->RemoveRouteElement(19, TempPDE.HLoc, TempPDE.VLoc, TempPDE.GetELink());
9378  // remove the last element under LeadElement
9379  }
9380  AllRoutes->RebuildRailwayFlag = true;
9381  // to force ClearandRebuildRailway at next clock tick if not in zoom-out mode
9382  // now deal with a rear linked autosigs route
9383  if(Track->TrackElementAt(823, FirstTVPos).Conn[FirstELinkPos] > -1)
9384  {
9385  int LinkedRouteNumber = -1;
9386  if(AllRoutes->GetRouteTypeAndNumber(19, Track->TrackElementAt(824, FirstTVPos).Conn[FirstELinkPos],
9387  Track->TrackElementAt(825, FirstTVPos).ConnLinkPos[FirstELinkPos], LinkedRouteNumber) == TAllRoutes::AutoSigsRoute)
9388  {
9389  AllRoutes->GetFixedRouteAt(170, LinkedRouteNumber).SetRouteSignals(1);
9390  // this is ok as now we are setting signals from the start of the route
9391  }
9392  }
9393  }
9394  }
9395  }
9396  TrainVector.push_back(*NewTrain);
9397  Utilities->CallLogPop(731);
9398  return(true);
9399 }
9400 
9401 // ---------------------------------------------------------------------------
9402 
9403 int TTrainController::EntryPos(int Caller, int TrainIDIn, int TrackVectorNumber)
9404 {
9405  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",EntryPos," + AnsiString(TrainIDIn) + "," +
9406  AnsiString(TrackVectorNumber));
9407  int VecPos = -1;
9408 
9409  for(unsigned int x = 0; x < TrainVector.size(); x++)
9410  {
9411  if(TrainVectorAt(1, x).TrainID == TrainIDIn)
9412  {
9413  VecPos = x;
9414  }
9415  }
9416  if(VecPos == -1)
9417  {
9418  throw Exception("Error, VecPos not set in EntryPos");
9419  }
9420  if(TrainVectorAt(2, VecPos).LeadElement == TrackVectorNumber)
9421  {
9422  Utilities->CallLogPop(734);
9423  return(TrainVectorAt(3, VecPos).LeadEntryPos);
9424  }
9425  else if(TrainVectorAt(4, VecPos).MidElement == TrackVectorNumber)
9426  {
9427  Utilities->CallLogPop(735);
9428  return(TrainVectorAt(5, VecPos).MidEntryPos);
9429  }
9430  else if(TrainVectorAt(6, VecPos).LagElement == TrackVectorNumber)
9431  {
9432  Utilities->CallLogPop(736);
9433  return(TrainVectorAt(7, VecPos).LagEntryPos);
9434  }
9435  Utilities->CallLogPop(737);
9436  return(-1);
9437 }
9438 
9439 // ---------------------------------------------------------------------------
9440 
9442 {
9443  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",TrainVectorAtIdent," + AnsiString(TrainID));
9444  for(unsigned int x = 0; x < TrainVector.size(); x++)
9445  {
9446  if(TrainVectorAt(53, x).TrainID == TrainID)
9447  {
9448  Utilities->CallLogPop(738);
9449  return(TrainVectorAt(54, x));
9450  }
9451  }
9452  throw Exception("Error - No Train identified in TrainVectorAtIdent with ID = " + AnsiString(TrainID));
9453 }
9454 
9455 // ---------------------------------------------------------------------------
9456 
9457 bool TTrainController::TrainExistsAtIdent(int Caller, int TrainID)
9458 // return true if find the train (added at v2.4.0 as can select a removed train
9459 // in OAListBox before it updates - reported by LiWinDom in error report 23/04/20)
9460 {
9461  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",TrainExistsAtIdent," + AnsiString(TrainID));
9462  for(unsigned int x = 0; x < TrainVector.size(); x++)
9463  {
9464  if(TrainVectorAt(69, x).TrainID == TrainID)
9465  {
9466  Utilities->CallLogPop(2152);
9467  return(true);
9468  }
9469  }
9470  Utilities->CallLogPop(2153);
9471  return(false);
9472 }
9473 
9474 // ---------------------------------------------------------------------------
9475 
9476 TDateTime TTrainController::GetControllerTrainTime(int Caller, TDateTime Time, int RepeatNumber, int IncrementalMinutes)
9477 {
9478  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",GetControllerTrainTime," + AnsiString(RepeatNumber) + "," +
9479  Utilities->Format96HHMMSS(Time));
9480  TDateTime RepeatTime = TrainController->GetRepeatTime(47, Time, RepeatNumber, IncrementalMinutes);
9481 
9482  Utilities->CallLogPop(2061);
9483  return(RepeatTime);
9484 }
9485 
9486 // ---------------------------------------------------------------------------
9487 
9488 AnsiString TTrainController::ContinuationEntryFloatingTTString(int Caller, TTrainDataEntry *TTDEPtr, int RepNum, int IncMins, int IncDig)
9489 // Enter with Ptr pointing to first action to be listed (i.e. next action)
9490 {
9491  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",ContinuationEntryFloatingTTString" + "," + TTDEPtr->HeadCode);
9492  AnsiString RetStr = "", PartStr = "";
9493  int Count = 0;
9494  TActionVectorIterator Ptr = TTDEPtr->ActionVector.begin();
9495 
9496  Ptr--; // because incremented at start of loop
9497  do
9498  {
9499  Ptr++;
9500  if((Ptr->Command != "") && (Ptr->Command[1] == 'S'))
9501  {
9502  continue; // move past the starting entry
9503  }
9504  if((Ptr->FormatType == Repeat) || Ptr >= TTDEPtr->ActionVector.end())
9505  {
9506  break;
9507  }
9508  if(Ptr->SignallerControl)
9509  {
9510  RetStr = "Train under signaller control";
9511  break;
9512  }
9513  if(Ptr->FormatType == TimeTimeLoc)
9514  {
9515  if(Ptr->ArrivalTime == Ptr->DepartureTime)
9516  {
9517  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(0, Ptr->ArrivalTime, RepNum, IncMins)) + ": Arrive & depart from " + Ptr->LocationName;
9518  }
9519  else
9520  {
9521  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(1, Ptr->ArrivalTime, RepNum, IncMins)) + ": Arrive at " + Ptr->LocationName + '\n' +
9522  Utilities->Format96HHMM(GetControllerTrainTime(2, Ptr->DepartureTime, RepNum, IncMins)) + ": Depart from " + Ptr->LocationName;
9523  Count++; // because there are 2 entries
9524  }
9525  }
9526  else if((Ptr->FormatType == TimeLoc) && (Ptr->ArrivalTime != TDateTime(-1)))
9527  {
9528  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(3, Ptr->ArrivalTime, RepNum, IncMins)) + ": Arrive at " + Ptr->LocationName;
9529  }
9530  else if((Ptr->FormatType == TimeLoc) && (Ptr->ArrivalTime == TDateTime(-1)))
9531  {
9532  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(4, Ptr->DepartureTime, RepNum, IncMins)) + ": Depart from " + Ptr->LocationName;
9533  }
9534  else if(Ptr->FormatType == PassTime) // new
9535  {
9536  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(5, Ptr->EventTime, RepNum, IncMins)) + ": Pass " + Ptr->LocationName;
9537  }
9538  else if(Ptr->Command == "Fns")
9539  {
9540  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(6, Ptr->EventTime, RepNum, IncMins)) + ": Form new service " +
9541  TrainController->GetRepeatHeadCode(46, Ptr->OtherHeadCode, RepNum, IncDig) + " at " + Ptr->LocationName;
9542  PartStr = ControllerCheckNewServiceDepartureTime(0, Ptr, RepNum, TTDEPtr, Ptr->LinkedTrainEntryPtr, IncMins, PartStr); //if there is a next service this adds the new service departure time to PartStr
9543  }
9544  else if(Ptr->Command == "F-nshs")
9545  {
9546  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(7, Ptr->EventTime, RepNum, IncMins)) + ": Form new service " +
9547  Ptr->NonRepeatingShuttleLinkHeadCode + " at " + Ptr->LocationName;
9548  PartStr = ControllerCheckNewServiceDepartureTime(1, Ptr, 0, TTDEPtr, Ptr->LinkedTrainEntryPtr, IncMins, PartStr); //if there is a next service this adds the new service departure time to RetStr
9549  //note that use LinkedTrainEntryPtr and not NonRepeatingShuttleLinkEntryPtr because the forward link from the feeder is LinkedTrainEntryPtr.
9550  //NonRepeatingShuttleLinkEntryPtr is in the shuttle's ActionVector to point back to the feeder.
9551  //NonRepeatingShuttleLinkEntryPtr is used below from the last shuttle as the forward link to the finishing service
9552  }
9553 //Since this is a new continuation entry service it can't be Fns-sh or Frh-sh but leave these in for consistency with TTrain::FloatingTimetableString
9554  else if((Ptr->Command == "Fns-sh") && (RepNum < (TTDEPtr->NumberOfTrains - 1))) // not the last repeat number
9555  {
9556  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(8, Ptr->EventTime, RepNum, IncMins)) + ": Form new service " +
9557  TrainController->GetRepeatHeadCode(47, Ptr->OtherHeadCode, RepNum + 1, IncDig) + " at " + Ptr->LocationName;
9558  // use RepNum+1 because it's the repeat number of the NEXT shuttle service that is relevant
9559  PartStr = ControllerCheckNewServiceDepartureTime(2, Ptr, RepNum + 1, TTDEPtr, Ptr->LinkedTrainEntryPtr, IncMins, PartStr); //if there is a next service this adds the new service departure time to RetStr
9560  }
9561  else if((Ptr->Command == "Fns-sh") && (RepNum >= (TTDEPtr->NumberOfTrains - 1))) // last repeat number
9562  {
9563  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(9, Ptr->EventTime, RepNum, IncMins)) + ": Form new service " +
9564  Ptr->NonRepeatingShuttleLinkHeadCode, +" at " + Ptr->LocationName;
9565  PartStr = ControllerCheckNewServiceDepartureTime(3, Ptr, 0, TTDEPtr, Ptr->NonRepeatingShuttleLinkEntryPtr, IncMins, PartStr); //if there is a next service this adds the new service departure time to RetStr
9566  }
9567  else if((Ptr->Command == "Frh-sh") && (RepNum < (TTDEPtr->NumberOfTrains - 1))) // not the last repeat number
9568  {
9569  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(10, Ptr->EventTime, RepNum, IncMins)) + ": Form new service " +
9570  TrainController->GetRepeatHeadCode(48, Ptr->OtherHeadCode, RepNum + 1, IncDig) + " at " + Ptr->LocationName;
9571  // use RepNum+1 because it's the repeat number of the NEXT shuttle service that is relevant
9572  PartStr = ControllerCheckNewServiceDepartureTime(4, Ptr, RepNum + 1, TTDEPtr, Ptr->LinkedTrainEntryPtr, IncMins, PartStr); //if there is a next service this adds the new service departure time to RetStr
9573  }
9574  else if((Ptr->Command == "Frh-sh") && (RepNum >= (TTDEPtr->NumberOfTrains - 1))) // last repeat number
9575  {
9576  PartStr = "Terminate at " + Ptr->LocationName;
9577  }
9578  else if(Ptr->Command == "Frh")
9579  {
9580  PartStr = "Terminate at " + Ptr->LocationName;
9581  }
9582  else if(Ptr->Command == "Fer")
9583  {
9584  AnsiString AllowedExits;
9585  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(11, Ptr->EventTime, RepNum, IncMins)) + ": Exit railway" +
9586  TrainController->GetExitLocationAndAt(3, Ptr->ExitList, AllowedExits) + AllowedExits;
9587  }
9588  else if(Ptr->Command == "Fjo")
9589  {
9590  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(12, Ptr->EventTime, RepNum, IncMins)) + ": Join " + TrainController->GetRepeatHeadCode(49,
9591  Ptr->OtherHeadCode, RepNum, IncDig) + " at " + Ptr->LocationName;
9592  }
9593  else if(Ptr->Command == "jbo")
9594  {
9595  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(13, Ptr->EventTime, RepNum, IncMins)) + ": Joined by " + TrainController->GetRepeatHeadCode
9596  (50, Ptr->OtherHeadCode, RepNum, IncDig) + " at " + Ptr->LocationName;
9597  }
9598  else if(Ptr->Command == "fsp")
9599  {
9600  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(14, Ptr->EventTime, RepNum, IncMins)) + ": Front split to " +
9601  TrainController->GetRepeatHeadCode(51, Ptr->OtherHeadCode, RepNum, IncDig) + " at " + Ptr->LocationName;
9602  }
9603  else if(Ptr->Command == "rsp")
9604  {
9605  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(15, Ptr->EventTime, RepNum, IncMins)) + ": Rear split to " +
9606  TrainController->GetRepeatHeadCode(52, Ptr->OtherHeadCode, RepNum, IncDig) + " at " + Ptr->LocationName;
9607  }
9608  else if(Ptr->Command == "cdt")
9609  {
9610  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(16, Ptr->EventTime, RepNum, IncMins)) + ": Change direction at " + Ptr->LocationName;
9611  }
9612  if(RetStr != "")
9613  {
9614  RetStr = RetStr + '\n' + PartStr;
9615  }
9616  else
9617  {
9618  RetStr = PartStr;
9619  }
9620  Count++;
9621  }
9622  while(Ptr < TTDEPtr->ActionVector.end() && (Count < 33) && ((Ptr->Command == "") || ((Ptr->Command != "") && (Ptr->Command[1] != 'F'))));
9623  // limit of 33 allows a max of 34 entries (may have gone from 32 to 34 because of a TimeTimeLoc), which with track and train status gives
9624  // a max of 48 lines, at 13 pixels each, = 624 pixels & screen height has 641 so will fit comfortably. Also 34 timetable entries is as far
9625  // forward as anyone should wish to see without looking at the full timetable
9626  Utilities->CallLogPop(2072);
9627  return(RetStr);
9628 }
9629 
9630 // ---------------------------------------------------------------------------
9631 
9632 AnsiString TTrainController::ControllerCheckNewServiceDepartureTime(int Caller, TActionVectorIterator Ptr, int RptNum, TTrainDataEntry *TDEPtr, TTrainDataEntry *LinkedTrainDataPtr, int IncrementalMinutes, AnsiString RetStr)
9633 {
9634  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + "," + AnsiString(Ptr - &TDEPtr->ActionVector.front()) + ","
9635  + AnsiString(RptNum) + ",ControllerCheckNewServiceDepartureTime," + TDEPtr->HeadCode);
9636  AnsiString DepTime = "", EventTime = "";
9637  bool CDTFlag = false; //reports if train changes direction before departs
9638  TActionVector NewServiceAV = LinkedTrainDataPtr->ActionVector;
9639  for(TActionVectorIterator AVI = NewServiceAV.begin(); AVI < NewServiceAV.end(); AVI++)
9640  {
9641  if(AVI->Command == "cdt")
9642  {
9643  CDTFlag = !CDTFlag; //toggles flag - allows for there being more than one cdt before departure
9644  continue;
9645  }
9646  if((AVI->Command == "fsp") || (AVI->Command == "rsp"))
9647  {
9648  EventTime = Utilities->Format96HHMM(TrainController->GetControllerTrainTime(21, AVI->EventTime, RptNum, IncrementalMinutes));
9649  RetStr += "\nNew service splits at " + EventTime;
9650  Utilities->CallLogPop(2237);
9651  return(RetStr);
9652  }
9653  if(AVI->Command == "jbo")
9654  {
9655  EventTime = Utilities->Format96HHMM(TrainController->GetControllerTrainTime(22, AVI->EventTime, RptNum, IncrementalMinutes));
9656  RetStr += "\nNew service joined by " + AVI->OtherHeadCode + " at " + EventTime;
9657  Utilities->CallLogPop(2238);
9658  return(RetStr);
9659  }
9660  if((AVI->FormatType == TimeLoc) && (AVI->DepartureTime > TDateTime(-1))) //departure time set
9661  {
9662  DepTime = Utilities->Format96HHMM(TrainController->GetControllerTrainTime(23, AVI->DepartureTime, RptNum, IncrementalMinutes));
9663  if(CDTFlag)
9664  {
9665  RetStr += "\nNew service changes direction then departs at " + DepTime;
9666  }
9667  else
9668  {
9669  RetStr += "\nNew service departs at " + DepTime;
9670  }
9671  Utilities->CallLogPop(2239);
9672  return(RetStr);
9673  }
9674  }
9675  Utilities->CallLogPop(2223);
9676  return(RetStr);
9677 }
9678 
9679 // ---------------------------------------------------------------------------
9680 // $$$$$$$$$$$$$$$$$$$$$$ Start of Timetable Functions $$$$$$$$$$$$$$$$$$$$$$$$
9681 /*
9682  Note: The terms 'action' and 'entry' have been used freely for individual code lines within services in comments & in variable names, but
9683  for messages and in the manual and help files the term Entry is reserved for a complete service or train (i.e. an entry in the timetable),
9684  and 'event' is reserved for and individual code line within a service. Repeats use the term 'item' if they use any at all.
9685 
9686  In references to 'HeadCode' can have an optional prefix - up to 4 additional characters that can be anything, so long as last 4 digits
9687  represent the headcode. This allows links to be uniquely identified regardless of the headcode - so can have same headcodes as often as
9688  user wishes
9689 
9690  Prior to start time, anything except a line beginning with a time [...leading spaces...] HH:MM is ignored - can be
9691  descriptive text or anything user wishes
9692  A time on its own line [HH:MM], with or without leading spaces, but with anything following it before the CR (which will
9693  be ignored) is taken as the timetable start time.
9694  Thereafter there must be text on every line in the timetable, as the first blank line (or end of file) will be taken as the end of the
9695  timetable. Text can follow the 'end of timetable' blank line if the user wishes.
9696  A line within the timetable beginning with '*', with or without leading spaces, is ignored. Such lines can add text
9697  within the timetable if required.
9698  Timetable entries consist of one line per headcode (i.e. per service, not necessarily per train, as one train can run several different
9699  services)
9700  Each line starts with HeadCode & full train information for a new train (Snt or Snt-sh), or, for a continuing service
9701  (Sfs, Sns, Sns-sh or Sns-fsh), can have (a) Headcode only or (b) HeadCode + Description, nothing else
9702 
9703  All leading & trailing spaces before & after a line or any entry in a line are stripped off - these can be included to make reading a
9704  text timetable file easier
9705 
9706  form:-
9707  HeadCode[;Description (plain text, no commas or semicolons)][;StartSpeed(kph); MaxRunningSpeed(kph); Mass(tonnes, prog converts to kg);
9708  MaxBrakeRate(tonnes force, prog converts to m/s/s); & gross power(kW, prog converts to power at rail in w)
9709  then multiple entries, separated by commas, of the form:-
9710 
9711  HH:MM;Snt;RearStartIdent FrontStartIdent }StartNew }
9712  HH:MM;Snt-sh;RearStartIdent FrontStartIdent;Fsh HeadCode }SNTShuttle }
9713  HH:MM;Sns-sh;Fxx-sh HeadCode;F-nshs HeadCode (non-repeating)}SNSShuttle }
9714 
9715  HH:MM;Command;HeadCode (Sfs Sns jbo fsp rsp Fns Fjo Frh-sh) }TimeCmdHeadCode } Train action entries
9716  HH:MM;F-nshs;NonRepeatingShuttleLinkHeadCode }FNSNonRepeatToShuttle }
9717  HH:MM;Sns-fsh;NonRepeatingShuttleLinkHeadCode }SNSNonRepeatFromShuttle }
9718 
9719  HH:MM;Command (cdt) }TimeCmd }
9720  HH:MM;Location (arr & dep) }TimeLoc }
9721  HH:MM;HH:MM;Location }TimeTimeLoc }
9722  HH:MM;pas;Location }PassTime }
9723  HH:MM;Fns-sh;Snx-sh HeadCode;Sns-fsh HeadCode (non-rep) }FSHNewService }
9724  HH:MM;Fer;set of allowable IDs }ExitRailway }
9725  Command (Frh only) }FinRemHere }
9726 
9727  R;mm;dd;nn. Repeat Repeat entry
9728 
9729  Formats:
9730 
9731  Command only: Frh
9732  Time;Command: cdt
9733  Time;Command;Headcode: Sfs Sns jbo fsp rsp Fns Fjo Frh-sh F-nshs Sns-fsh
9734  Time;Command;2 Element IDs: Snt
9735  Time;Comand;n Element IDs: Fer
9736  Time;Command;rep Headcode;nonrep Headcode: Sns-sh Fns-sh
9737  Time;Command;2 Element IDs;Headcode Snt-sh
9738  Time;Command;Location pas
9739  Time;Location Arr Dep
9740  Time;Time;Location Arr & dep together
9741 
9742  9 Single entries: Snt (located or unlocated); pas; cdt; TimeLoc arr & dep; TimeTimeLoc; Fer; Frh
9743 
9744  9 1x Linked entries: Non-shuttle: fsp or rsp -> Sfs; Fns -> Sns; Fjo -> jbo; times must match, headcodes must match
9745  Shuttle: F-nshs -> Sns-sh: times match, F-nshs HeadCode matches Sns-sh 2nd Headcode;
9746  Fns-sh -> Sns-fsh: Fns-sh time + all repeats = Sns-fsh time, Fns-sh 2nd headcode matches Sns-fsh Headcode
9747 
9748  4 2x Linked entries, all shuttles:
9749 
9750  Frh-sh -> Snt-sh: Frh-sh time = Snt-sh time + 1 repeat while repeating, Frh-sh Headcode = Snt-sh Headcode;
9751  -> Sns-sh: Frh-sh time = Sns-sh time + 1 repeat while repeating, Frh-sh Headcode = Sns-sh 1st Headcode;
9752  -> Remain Here (at finish location after all repeats)
9753  Fns-sh -> Snt-sh: Frh-sh time = Snt-sh time + 1 repeat while repeating, Fns-sh 1st Headcode = Snt-sh Headcode
9754  -> Sns-sh: Frh-sh time = Sns-sh time + 1 repeat while repeating, Fns-sh 1st Headcode = Sns-sh 1st Headcode
9755 
9756  Allowable successors:-
9757 
9758  Successor state Type
9759 
9760  Snt located AtLoc ) Snt AtLoc successors: TimeLoc dep/jbo/fsp/rsp/cdt/Frh/Fns/Fjo/Frh-sh/Fns-sh/F-nshs;
9761  Snt Unlocated Moving ) Snt Moving successors: TimeLoc arr/TimeTimeLoc/pas/Fer;
9762  Sfs AtLoc )
9763  Sns AtLoc ) Start
9764  Sns-fsh AtLoc )
9765  Snt-sh AtLoc )
9766  Sns-sh AtLoc )
9767 
9768  pas Moving )
9769  jbo AtLoc )
9770  fsp AtLoc )
9771  rsp AtLoc ) Intermediate
9772  cdt AtLoc )
9773  TimeLoc arr Moving (bef) )
9774  TimeLoc dep AtLoc (bef) )
9775  TimeTimeLoc Moving )
9776 
9777  Fns Repeat/Nothing)
9778  Fjo Repeat/Nothing)
9779  Frh Repeat/Nothing)
9780  Fer Repeat/Nothing) Finish
9781  Frh-sh Repeat )
9782  Fns-sh Repeat )
9783  F-nshs Nothing )
9784 
9785  Descriptions:
9786  Snt New train
9787  Sfs New service from split
9788  Sns New service from another service
9789  Sns-fsh New non-repeating service from a shuttle service
9790  Snt-sh New shuttle train at a timetabled stop
9791  Sns-sh New shuttle service from a feeder service
9792 
9793  pas Pass
9794  jbo Be joined by another train
9795  fsp Front split
9796  rsp Rear split
9797  cdt Change direction of train
9798  TimeLoc arr Arrival
9799  TimeLoc dep Departure
9800  TimeTimeLoc Arrival and departure
9801 
9802  Fns Finish & form a new service
9803  Fjo Finish & join another train
9804  Frh Finish & remain here
9805  Fer Finish & exit railway
9806  Frh-sh Finish & repeat shuttle, finally remain here
9807  Fns-sh Finish & repeat shuttle, finally form a non-repeating service
9808  F-nshs Finish & form a shuttle feeder service
9809 */
9810 
9811 bool TTrainController::TimetableIntegrityCheck(int Caller, char *FileName, bool GiveMessages, bool CheckLocationsExistInRailway) // true for success
9812 {
9813  // Error messages mainly given in called functions, five are given here - empty file; inability to find a start time; timetable containing
9814  // a line that is too long; timetable containing too few lines; and timetable failed to open.
9815  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",TimetableIntegrityCheck," + AnsiString(FileName));
9816  // new for v0.2b
9817  // compile ActiveTrackElementNameMap
9818  TTrack::TActiveTrackElementNameMapEntry ActiveTrackElementNameMapEntry;
9819 
9821  for(unsigned int x = 0; x < Track->TrackVector.size(); x++)
9822  {
9823  // if((Track->TrackElementAt(, x).ActiveTrackElementName != "") && (Track->TrackElementAt(, x).TrackType != Continuation))
9825  == Track->ContinuationNameMap.end())
9826  {
9827  // exclude any name that appears in a continuation, error message given in tt validation if try to include such a name in a tt
9828  ActiveTrackElementNameMapEntry.first = Track->TrackElementAt(1035, x).ActiveTrackElementName;
9829  ActiveTrackElementNameMapEntry.second = 0; // this is a dummy value
9830  Track->ActiveTrackElementNameMap.insert(ActiveTrackElementNameMapEntry);
9831  }
9832  }
9834  // end of new section
9835  std::ifstream TTBLFile(FileName, std::ios_base::binary);
9836 
9837  // binary mode so the "\r\n" pairs stay as they are rather than being entered as '\n'
9838  if(TTBLFile.is_open())
9839  {
9840  char *TrainTimetableString = new char[10000];
9841  // enough for over 200 stations, should be adequate!
9842  bool EndOfFile = false;
9843  int Count = 0;
9844  // counts 'relevant' lines, i.e ignores any before the start time on its own line
9845  TTBLFile.getline(TrainTimetableString, 10000, '\0');
9846  // delimiter is '\0' as it's an AnsiString
9847  if(TTBLFile.eof() && (TrainTimetableString[0] == '\0'))
9848  // file empty - stores a null in 1st position if doesn't load any characters
9849  {
9850  // may still have eof even if read a line (no CRLF at end), and
9851  // if so need to process it
9852  TimetableMessage(GiveMessages, "Timetable invalid - file empty");
9853  TTBLFile.close();
9854  delete[] TrainTimetableString;
9855  Utilities->CallLogPop(1611);
9856  return(false);
9857  }
9858  AnsiString OneLine(TrainTimetableString);
9859  bool FinalCallFalse = false;
9860  while((Count == 0) && !ProcessOneTimetableLine(5, Count, OneLine, EndOfFile, FinalCallFalse, GiveMessages, CheckLocationsExistInRailway))
9861  // get rid of lines before the start time
9862  {
9863  // ProcessOneTimetableLine returns true for a valid start time, an EndOfFile &/or a blank entry
9864  TTBLFile.getline(TrainTimetableString, 10000, '\0');
9865  if(TTBLFile.eof() && (TrainTimetableString[0] == '\0'))
9866  // stores a null in 1st position if doesn't load any characters
9867  {
9868  // may still have eof even if read a line (no CRLF at end), and
9869  // if so need to process it
9870  TimetableMessage(GiveMessages, "Timetable invalid - unable to find a valid start time on its own line");
9871  TTBLFile.close();
9872  delete[] TrainTimetableString;
9873  Utilities->CallLogPop(772);
9874  return(false);
9875  }
9876  OneLine = AnsiString(TrainTimetableString);
9877  }
9878  // here when have accepted the start time
9879  Count++; // increment past the start time
9880  while(!EndOfFile)
9881  {
9882  TTBLFile.getline(TrainTimetableString, 10000, '\0');
9883  // get next line after start time
9884  if(TTBLFile.eof() && (TrainTimetableString[0] == '\0'))
9885  // stores a null in 1st position if doesn't load any characters
9886  {
9887  // may still have eof even if read a line (no CRLF at end), and
9888  // if so need to process it
9889  EndOfFile = true;
9890  OneLine = "";
9891  }
9892  else
9893  {
9894  OneLine = AnsiString(TrainTimetableString);
9895  }
9896  if(OneLine.Length() > 9999)
9897  {
9898  TimetableMessage(GiveMessages, "Timetable contains a line that is too long - 10,000 or more characters!");
9899  TTBLFile.close();
9900  delete[] TrainTimetableString;
9901  Utilities->CallLogPop(789);
9902  return(false);
9903  }
9904  bool FinalCallFalse = false;
9905  if(!ProcessOneTimetableLine(6, Count, OneLine, EndOfFile, FinalCallFalse, GiveMessages, CheckLocationsExistInRailway))
9906  // false for FinalCall - just checking at this stage
9907  {
9908  TTBLFile.close();
9909  delete[] TrainTimetableString;
9910  Utilities->CallLogPop(770);
9911  return(false);
9912  }
9913  if(EndOfFile && (Count < 2))
9914  // Timetable must contain at least two relevant lines, one for start time and at least one train
9915  {
9916  TimetableMessage(GiveMessages, "Timetable has too few or no relevant entries - must have a start time on its own line and at least one train");
9917  TTBLFile.close();
9918  delete[] TrainTimetableString;
9919  Utilities->CallLogPop(771);
9920  return(false);
9921  }
9922  Count++;
9923  }
9924  delete[] TrainTimetableString;
9925  TTBLFile.close();
9926  } // if(TTBLFile.is_open())
9927  else
9928  {
9929  TimetableMessage(GiveMessages, "Failed to open timetable file, make sure it's spelled correctly, it exists and isn't open in another application");
9930  Utilities->CallLogPop(2154);
9931  return(false);
9932  }
9933  Utilities->CallLogPop(753);
9934  return(true);
9935 }
9936 
9937 // ---------------------------------------------------------------------------
9938 
9939 bool TTrainController::ProcessOneTimetableLine(int Caller, int Count, AnsiString OneLine, bool &EndOfFile, bool FinalCall, bool GiveMessages,
9940  bool CheckLocationsExistInRailway) // return true for success
9941 
9942 /* Format:
9943  Prior to start time, anything except a line beginning with a time [...leading spaces...] HH:MM is ignored - can be
9944  descriptive text or anything user wishes
9945  A time on its own line [HH:MM], with or without leading spaces, but with anything following it before the CR (which will
9946  be ignored) is taken as the timetable start time.
9947  Thereafter there must be text on every line in the timetable, as the first blank line (or end of file) will be taken as the end of the
9948  timetable. Text can follow the 'end of timetable' blank line if the user wishes.
9949  A line within the timetable beginning with '*', with or without leading spaces, is ignored. Such lines can add text
9950  within the timetable if required.
9951  Timetable entries consist of one line per headcode (i.e. per service, not necessarily per train, as one train can run several different
9952  services)
9953  Each line starts with HeadCode & full train information for a new train (Snt or Snt-sh), or, for a continuing service
9954  (Sfs, Sns, Sns-sh or Sns-fsh), can have (a) Headcode only or (b) HeadCode + Description, nothing else
9955 
9956  All leading & trailing spaces before & after a line or any entry in a line are stripped off - these can be included to make reading a
9957  text timetable file easier
9958 
9959  form:-
9960  HeadCode[;Description (plain text, no commas or semicolons)][;StartSpeed(kph); MaxRunningSpeed(kph); Mass(tonnes, prog converts to kg);
9961  MaxBrakeRate(tonnes force, prog converts to m/s/s); & gross power(kW, prog converts to power at rail in w)
9962  then multiple entries, separated by commas, of the form:-
9963 
9964  Format FormatType
9965  [W]HH:MM;Command (cdt) }TimeCmd }
9966  [W]HH:MM;Fer;set of allowable IDs }ExitRailway }
9967  [W]HH:MM;pas;Location }PassTime }
9968  [W]HH:MM;Snt;RearStartIdent FrontStartIdent }StartNew }
9969  [W]HH:MM;Command;HeadCode (Sfs Sns jbo fsp rsp Fns Fjo Frh-sh) }TimeCmdHeadCode }
9970  [W]HH:MM;F-nshs;non-repeating headcode }FNSNonRepeatToShuttle }
9971  [W]HH:MM;Sns-fsh;NonRepeatingShuttleLinkHeadCode }SNSNonRepeatFromShuttle } Train action entries
9972  [W]HH:MM;Snt-sh;RearStartIdent FrontStartIdent;FSH HeadCode }SNTShuttle }
9973  [W]HH:MM;Sns-sh;FSH HeadCode;F-nshs HeadCode (non-repeating) }SNSShuttle }
9974  [W]HH:MM;Fns-sh;Details }FSHNewService }
9975  [W]HH:MM;Location (arr & dep) }TimeLoc }
9976  [W]HH:MM;HH:MM;Location }TimeTimeLoc }
9977  Command (Frh only) }FinRemHere }
9978 
9979  R;mm;dd;nn. Repeat Repeat entry
9980 
9981  Two times represent arrival & departure, without any other events between (if arrival and departure times are the same
9982  then departure is 30 sec after arrival), single time represents (a) event time; (b) arrival time if train not already
9983  at location; or (c) departure time if train already at location (including train started at location either as a new
9984  train or as a continuation service train at that location). All lines must contain a start entry and a finish entry,
9985  the finish being the last unless there is a repeat entry. The repeat entry begins with 'R', then the incremental
9986  minutes, incremental train headcode last 2 digits, and number of repeats.
9987 
9988  Shuttle entries are where can loop back to an earlier Snt-sh or Sns-sh entry from a Frh-sh or Fns-sh (Finish Shuttle)
9989  entry. Here the shuttle start can have two entries, one from a set position (Snt-sh, must be located) or from a F-nshs
9990  (Sns-sh) - with NO repeat from this source, and from a Fxx-sh, with repeats. After all shuttle repeats Frh-sh remains
9991  where it is, and Fns-sh links to a new service (via an Sns entry), but there must be no repeats in this new service
9992  (it's for a shuttle train to return to depot at end of services)
9993 
9994  Command/Location & details are as follows:-
9995 
9996  Although headcodes can be duplicated, all joins, splits, new services etc give other headcode from both trains' povs, and
9997  these have to match once only, i.e. if 2E44 splits to 2E45 then it can't split to 2E45 anywhere else, and 2E45 must give
9998  2E44 in its Sfs entry. All these are checked.
9999  ***add note re shuttles & their use of otherheadcodes + non-repeating headcodes***
10000 
10001  Start commands:-
10002  Snt (StartNew) = Start New Train, i.e. create new train, details = rearstartident, space, frontstartident (can't confuse
10003  with loc as a start entry can't have a location as details)
10004  Sfs (TimeCmdHeadCode) = Start From Split, create a new train that has split from another train (& listed in other train's
10005  timetable line), details = other headcode - (can't confuse with loc as start can't be a loc)
10006  Sns (TimeCmdHeadCode) = Start, headcode change from earlier service - no need to create train as already exists, it just
10007  changes its relevant information, details = old headcode (can't confuse with loc as start can't be a loc)
10008  Snt-sh (SNTShuttle) = Start New Train, i.e. create new train, details = rearstartident, space, frontstartident (can't
10009  confuse with loc as start can't be a loc) then the Fsh-XX service headcode (OtherHeadCode can't be same headcode)
10010  Sns-sh (SNSShuttle) = Start, headcode change from earlier service - no need to create train as already exists, it just
10011  changes its relevant information, details = the FSH-XX service headcode (OtherHeadCode, can't be same headcode)
10012  followed by the non-repeating F-nshs headcode (NonRepeatingShuttleLinkHeadCode)
10013  Sns-fsh (SNSNonRepeatFromShuttle) = Start as a non-repeating service from a shuttle service that has finished all its
10014  repeats, details = NonRepeatingShuttleLinkHeadCode for the corresponding shuttle Fns-sh service
10015 
10016  Intermediate commands:-
10017  Time - Location (TimeLoc), can be arrival or departure depending on context
10018  Time Time location (TimeTimeLoc), arrival and departure
10019  Location Name (exactly as used in the railway) in TimeLoc & TimeTimeLoc means that the train is required to stop at the location
10020  pas (PassTime), Time;pas;Location
10021  jbo (TimeCmdHeadCode) = Joined By Other = joined by other train, details = new headcode (await other train - may be delayed). Note that the
10022  joining train's finish details must correspond or the file check will fail
10023  fsp (TimeCmdHeadCode) = Front Split = a new train splits away from front of this train, both trains in same direction, details = new headcode (create
10024  new train - that train's starting information must correspond)
10025  rsp (TimeCmdHeadCode) = Rear Split = a new train splits away from rear of this train, both trains in same direction, details = new headcode (create
10026  new train - that train's starting information must correspond)
10027  cdt (TimeCmd) = Change Direction of Train = change direction, no details needed & no train creation
10028 
10029  Finish commands:-
10030  Fns (TimeCmdHeadCode) = Finish New Service = finish, form new service in same direction, details = new headcode (no train
10031  creation)
10032  F-nshs (FNSShuttle) = Finish New Service (Shuttle) = finish, form new shuttle service in same direction, details =
10033  shuttle headcode (no train creation)
10034  Fjo (TimeCmdHeadCode) = Finish Join Other = finish, join other train (which must be on an adjacent element, either end -
10035  may have to wait for it), details = new headcode (delete train)
10036  Frh (FinRemHere) = Finish Remain Here = stay here indefinitely, no details & no time needed
10037  Fer (ExitRailway) = Finish, exit railway (i.e at a continuation) - details = set of allowable exit IDs
10038  Frh-sh (TimeCmdHeadCode) = Finish then restart as a shuttle using Snt-sh or Sns-sh, when all shuttle repeats done remain
10039  here
10040  Fns-sh (FSHNewService) = Finish then restart as a shuttle using Snt-sh or Sns-sh, when all shuttle repeats done form new
10041  service via Sns-fsh using the NonRepeatingShuttleLinkHeadCode
10042 
10043  Repeat:-
10044  R;mm;dd;nn (Repeat) where mm = minute increment, dd = 2nd 2 headcode digit increment & nn = no. of repeats (no max as can duplicate
10045  headcodes - it is up to user to avoid duplicates if he/she wishes to.
10046 
10047  Checks carried out with error messages in this function:-
10048  At least one comma in a service line (it's based on a .csv file)
10049  No entries following train information;
10050  At least one comma in remainder after train information (i.e at least a start and a finish entry);
10051  SplitEntry returns false in an intermediate entry - message repeats the entry for information;
10052  First entry not a start entry;
10053  Train information incomplete before a start entry;
10054  Entry follows a finish entry but doesn't begin with 'R';
10055  SplitEntry returns false in a finish entry - message repeats the entry for information;
10056  Last action entry isn't a finish entry.
10057 
10058  Function returns false with no message if:-
10059  Timetable start time invalid (no message is given for an invalid time as the line is assumed to be an irrelevant line; if no start
10060  time is found at all then an error message is given in the calling function);
10061  SplitTrainInfo returns false (message given in called function);
10062  SplitRepeat returns false (message given in called function).
10063 */{
10064  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",ProcessOneTimetableLine," + AnsiString(Count) + "," + OneLine + "," +
10065  AnsiString((short)FinalCall) + "," + AnsiString((short)CheckLocationsExistInRailway));
10066  TTrainDataEntry TempTrainDataEntry;
10067 
10068  EndOfFile = false;
10069  StripSpaces(0, OneLine);
10070  // strip both leading and trailing spaces at ends of line and spaces before and after all commas and
10071  // semicolons within the line
10072  ServiceReference = "";
10073  if(OneLine != "")
10074  {
10075  if(OneLine[1] != '*')
10076  {
10077  int SCPos = OneLine.Pos(';');
10078  if(SCPos == 0)
10079  {
10080  ServiceReference = OneLine.SubString(1, 8);
10081  }
10082  else
10083  {
10084  ServiceReference = OneLine.SubString(1, (SCPos - 1));
10085  }
10086  }
10087  }
10088  bool AllCommas = true;
10089 
10090  for(int x = 1; x < OneLine.Length() + 1; x++) // check for nothing but commas (may be all commas if created from Excel) or a blank line
10091  {
10092  if(OneLine[x] != ',')
10093  {
10094  AllCommas = false;
10095  }
10096  }
10097  if(AllCommas || (OneLine == ""))
10098  {
10099  if(Count > 0)
10100  {
10101  EndOfFile = true;
10102  // returns true for a blank line - treated as end of file
10103  Utilities->CallLogPop(1018);
10104  return(true);
10105  }
10106  else // count == 0 so not yet found a start time, no message to be given
10107  {
10108  Utilities->CallLogPop(754);
10109  return(false);
10110  }
10111  }
10112  AnsiString First = "", Second = "", Third = "", Fourth = "";
10113  int RearStartOrRepeatMins = 0, FrontStartOrRepeatDigits = 0, NumberOfRepeats = 0;
10114  TDateTime EventTime(0), ArrivalTime(0), DepartureTime(0);
10115  TDateTime StartTime(0);
10116  TNumList ExitList;
10117  bool Warning = false;
10118 
10119  if(Count == 0) // no start time found yet
10120  {
10121 /* dropped at v0.6b
10122  AnyHeadCodeValid = false;
10123  if(OneLine.SubString(6,5) == ";0000")
10124  {
10125  AnyHeadCodeValid = true;
10126  }
10127 */
10128  if(!CheckTimeValidity(0, OneLine, StartTime))
10129  {
10130  // no message is given for an invalid time as it's assumed to be an irrelevant line
10131  // if no start time is found at all then an error message is given in the calling function
10132  // AnyHeadCodeValid = false;
10133  Utilities->CallLogPop(755);
10134  return(false);
10135  }
10136  if(FinalCall) // here if start time valid
10137  {
10138  TTClockTime = StartTime;
10139  TimetableStartTime = StartTime;
10140  }
10141  }
10142  else
10143  {
10144  AnsiString TrainInfoStr = "", HeadCode = "", Description = "";
10145  int StartSpeed = 0, MaxRunningSpeed = 0, Mass = 0;
10146  double MaxBrakeRate = 0;
10147  double PowerAtRail = 0;
10148  int SignallerSpeed = 0;
10149  if(OneLine[1] == '*')
10150  {
10151  Utilities->CallLogPop(1581);
10152  return(true);
10153  // ignore any line beginning with '*' but return true as there is no error
10154  }
10155  int Pos = OneLine.Pos(',');
10156  if(Pos == 0)
10157  {
10158  int SubStringLength = 20;
10159  if(OneLine.Length() < 20)
10160  {
10161  SubStringLength = OneLine.Length();
10162  }
10163  TimetableMessage(GiveMessages, "Error in timetable - entry incomplete: see '" + OneLine.SubString(1, SubStringLength) + "'....");
10164  Utilities->CallLogPop(766);
10165  return(false);
10166  }
10167  TrainInfoStr = OneLine.SubString(1, Pos - 1);
10168  if(!SplitTrainInfo(0, TrainInfoStr, HeadCode, Description, StartSpeed, MaxRunningSpeed, Mass, MaxBrakeRate, PowerAtRail, SignallerSpeed,
10169  GiveMessages)) // error messages given in SplitTrainInfo
10170  {
10171  Utilities->CallLogPop(773);
10172  return(false);
10173  }
10174  if(FinalCall)
10175  {
10176  // store Train info - conversions done in SplitTrainInfo
10177  // only headcode mandatory for continuing services
10178  TempTrainDataEntry.HeadCode = HeadCode;
10179  TempTrainDataEntry.ServiceReference = HeadCode;
10180  TempTrainDataEntry.Description = Description;
10181  TempTrainDataEntry.StartSpeed = StartSpeed;
10182  TempTrainDataEntry.Mass = Mass;
10183  TempTrainDataEntry.MaxRunningSpeed = MaxRunningSpeed;
10184  TempTrainDataEntry.MaxBrakeRate = MaxBrakeRate;
10185  TempTrainDataEntry.PowerAtRail = PowerAtRail;
10186  TempTrainDataEntry.SignallerSpeed = SignallerSpeed;
10187  TTrainOperatingData TempTrainOperatingData;
10188  TempTrainDataEntry.TrainOperatingDataVector.push_back(TempTrainOperatingData); // push empty vector for now
10189  }
10190  AnsiString NewRemainder = OneLine.SubString(Pos + 1, OneLine.Length() - Pos);
10191  // now left with series of entries for this train, but there may be a string of commas at the end of the line if created by Excel
10192  // so strip them off
10193  while(NewRemainder[NewRemainder.Length()] == ',')
10194  {
10195  if(NewRemainder.Length() > 1)
10196  {
10197  NewRemainder = NewRemainder.SubString(1, NewRemainder.Length() - 1);
10198  }
10199  else
10200  {
10201  NewRemainder = "";
10202  break;
10203  }
10204  }
10205  // check if zero length & fail if so
10206  if(NewRemainder == "")
10207  {
10208  TimetableMessage(GiveMessages, "Error in timetable - no events following train: '" + OneLine + "'");
10209  Utilities->CallLogPop(769);
10210  return(false);
10211  }
10212  // now have one more entry than there are commas
10213  int CommaCount = 0;
10214  for(int x = 1; x < NewRemainder.Length() + 1; x++)
10215  {
10216  if(NewRemainder[x] == ',')
10217  {
10218  CommaCount++;
10219  }
10220  } // must have at least 1 comma, for start & finish entries, unless train is entered under signaller control
10221  if(CommaCount == 0)
10222  {
10223  if((NewRemainder.SubString(7, 3) != "Snt") || (NewRemainder[NewRemainder.Length()] != 'S'))
10224  {
10225  int SubStringLength = 20;
10226  if(OneLine.Length() < 20)
10227  {
10228  SubStringLength = OneLine.Length();
10229  }
10230  TimetableMessage(GiveMessages,
10231  "Error in timetable - must have at least a start and a finish event for a train that is not started under signaller control - see line beginning: '" +
10232  OneLine.SubString(1, SubStringLength) + "'....");
10233  Utilities->CallLogPop(783);
10234  return(false);
10235  }
10236  }
10237  AnsiString OneEntry = "";
10238  TTimetableFormatType FormatType;
10239  TTimetableSequenceType SequenceType;
10240  TTimetableLocationType LocationType;
10241  TTimetableShuttleLinkType ShuttleLinkType;
10242  bool FinishFlag = false;
10243  for(int x = 0; x < CommaCount + 1; x++)
10244  {
10245  if((CommaCount == 0) || (x < CommaCount))
10246  // i.e. train entered under signaller control with no repeats, or entry is not the last,
10247  // in which case there's a comma & finish element or repeat still to come this entry could
10248  // be a finish but can't be a repeat
10249  {
10250  if(CommaCount == 0)
10251  {
10252  OneEntry = NewRemainder;
10253  NewRemainder = "";
10254  }
10255  else
10256  {
10257  Pos = NewRemainder.Pos(',');
10258  OneEntry = NewRemainder.SubString(1, Pos - 1);
10259  NewRemainder = NewRemainder.SubString(Pos + 1, NewRemainder.Length() - Pos);
10260  }
10261  First = "";
10262  Second = "";
10263  Third = "";
10264  Fourth = "";
10265  RearStartOrRepeatMins = 0;
10266  FrontStartOrRepeatDigits = 0;
10267  NumberOfRepeats = 0;
10268  if(!SplitEntry(0, OneEntry, GiveMessages, CheckLocationsExistInRailway, First, Second, Third, Fourth, RearStartOrRepeatMins,
10269  FrontStartOrRepeatDigits, FormatType, LocationType, SequenceType, ShuttleLinkType, ExitList, Warning))
10270  {
10271  TimetableMessage(GiveMessages, "Error in timetable - Event: '" + OneEntry + "'");
10272  Utilities->CallLogPop(756);
10273  return(false);
10274  }
10275  // check if warning for Frh or Fjo & reject
10276  if(Warning && (Second == "Frh"))
10277  {
10278  TimetableMessage(GiveMessages, "Error in line - '" + OneEntry + "': warnings cannot be given for 'Frh' events");
10279  Utilities->CallLogPop(1793);
10280  return(false);
10281  }
10282  if(Warning && (Second == "Fjo"))
10283  {
10284  TimetableMessage(GiveMessages, "Error in line - '" + OneEntry +
10285  "': warnings cannot be given for 'Fjo' events, for a train join warning add a 'W' prefix to the 'jbo' event");
10286  Utilities->CallLogPop(1794);
10287  return(false);
10288  }
10289  if(x == 0) // should be start event
10290  {
10291  if(SequenceType != Start)
10292  {
10293  TimetableMessage(GiveMessages, "Error in timetable - First event not a start event: '" + OneEntry + "'");
10294  Utilities->CallLogPop(784);
10295  return(false);
10296  }
10297  if((Second == "Snt") && (Fourth == 'S') && (NewRemainder != ""))
10298  {
10299  if(NewRemainder[1] != 'R')
10300  {
10301  TimetableMessage(GiveMessages,
10302  "Error in timetable - the only event that can follow a train created under signaller control is a repeat, see '" +
10303  OneEntry + "'");
10304  Utilities->CallLogPop(787);
10305  return(false);
10306  }
10307  }
10308  if((Second == "Snt") || (Second == "Snt-sh"))
10309  // need full train information including non-default values for at least HeadCode, Description,
10310  // MaxRunningSpeed, Mass, MaxBrakeRate, & PowerAtRail
10311  {
10312  if((HeadCode == "") || (Description == "") || (MaxRunningSpeed == 0) || (Mass == 0) || (MaxBrakeRate == 0)) // ||
10313  // (PowerAtRail == 0)) allowed 0 for power at v2.4.0
10314  {
10315  TimetableMessage(GiveMessages, "Error in timetable - train information incomplete before 'Snt' or 'Snt-sh' start event: '" +
10316  OneEntry + "'");
10317  Utilities->CallLogPop(1783);
10318  return(false);
10319  }
10320  }
10321  if((Second == "Sfs") || (Second == "Sns") || (Second == "Sns-sh") || (Second == "Sns-fsh"))
10322  // service continuation - need at least non-default value for HeadCode
10323  {
10324  if(HeadCode == "")
10325  {
10326  TimetableMessage(GiveMessages, "Error in timetable - headcode missing before 'Sfs', 'Sns', 'Sns-sh' or 'Sns-fsh' start event: '" +
10327  OneEntry + "'");
10328  Utilities->CallLogPop(788);
10329  return(false);
10330  }
10331  if((StartSpeed != 0) || (MaxRunningSpeed != 0) || (Mass != 0) || (MaxBrakeRate != 0) || (PowerAtRail != 0))
10332  {
10333  TimetableMessage(GiveMessages,
10334  "Error in timetable - information additional to a headcode & optional description given before 'Sfs', 'Sns', 'Sns-sh' or 'Sns-fsh' start event: '" +
10335  OneEntry + "'");
10336  Utilities->CallLogPop(843);
10337  return(false);
10338  }
10339  }
10340  }
10341  if(SequenceType == Finish)
10342  {
10343  FinishFlag = true;
10344  // marker for only permitted additional entry being a repeat, only needed if the
10345  // finish entry is not the last entry
10346  }
10347  if(FinalCall)
10348  {
10349  // interpret & add to ActionVector
10350  TDateTime TempTime;
10351  TActionVectorEntry ActionVectorEntry;
10352  ActionVectorEntry.FormatType = FormatType;
10353  ActionVectorEntry.LocationType = LocationType;
10354  ActionVectorEntry.SequenceType = SequenceType;
10355  ActionVectorEntry.ShuttleLinkType = ShuttleLinkType;
10356  ActionVectorEntry.Warning = Warning;
10357  if(FormatType == TimeLoc)
10358  {
10359  if(CheckTimeValidity(1, First, ActionVectorEntry.EventTime))
10360  {
10361  ;
10362  } // these will all be true as final call
10363 
10364  ActionVectorEntry.LocationName = Second;
10365  }
10366  else if(FormatType == PassTime) // new
10367  {
10368  if(CheckTimeValidity(17, First, ActionVectorEntry.EventTime))
10369  {
10370  ;
10371  }
10372  ActionVectorEntry.Command = Second;
10373  ActionVectorEntry.LocationName = Third;
10374  }
10375  else if(FormatType == TimeTimeLoc)
10376  {
10377  if(CheckTimeValidity(2, First, ActionVectorEntry.ArrivalTime))
10378  {
10379  ;
10380  }
10381  if(CheckTimeValidity(3, Second, ActionVectorEntry.DepartureTime))
10382  {
10383  ;
10384  }
10385  ActionVectorEntry.LocationName = Third;
10386  }
10387  else if(FormatType == TimeCmd)
10388  {
10389  if(CheckTimeValidity(4, First, ActionVectorEntry.EventTime))
10390  {
10391  ;
10392  }
10393  ActionVectorEntry.Command = Second;
10394  }
10395  else if(FormatType == ExitRailway)
10396  {
10397  if(CheckTimeValidity(18, First, ActionVectorEntry.EventTime))
10398  {
10399  ;
10400  }
10401  ActionVectorEntry.Command = Second;
10402  ActionVectorEntry.ExitList = ExitList;
10403  }
10404  else if(FormatType == StartNew)
10405  {
10406  if(CheckTimeValidity(5, First, ActionVectorEntry.EventTime))
10407  {
10408  ;
10409  }
10410  ActionVectorEntry.Command = Second;
10411  ActionVectorEntry.RearStartOrRepeatMins = RearStartOrRepeatMins;
10412  ActionVectorEntry.FrontStartOrRepeatDigits = FrontStartOrRepeatDigits;
10413  if(Fourth == 'S')
10414  {
10415  ActionVectorEntry.SignallerControl = true;
10416  }
10417  }
10418  else if(FormatType == SNTShuttle)
10419  {
10420  if(CheckTimeValidity(6, First, ActionVectorEntry.EventTime))
10421  {
10422  ;
10423  }
10424  ActionVectorEntry.Command = Second;
10425  ActionVectorEntry.RearStartOrRepeatMins = RearStartOrRepeatMins;
10426  ActionVectorEntry.FrontStartOrRepeatDigits = FrontStartOrRepeatDigits;
10427  ActionVectorEntry.OtherHeadCode = Fourth;
10428  }
10429  else if(FormatType == SNSShuttle)
10430  {
10431  if(CheckTimeValidity(7, First, ActionVectorEntry.EventTime))
10432  {
10433  ;
10434  }
10435  ActionVectorEntry.Command = Second;
10436  ActionVectorEntry.OtherHeadCode = Third;
10437  ActionVectorEntry.NonRepeatingShuttleLinkHeadCode = Fourth;
10438  }
10439  else if(FormatType == TimeCmdHeadCode)
10440  {
10441  if(CheckTimeValidity(8, First, ActionVectorEntry.EventTime))
10442  {
10443  ;
10444  }
10445  ActionVectorEntry.Command = Second;
10446  ActionVectorEntry.OtherHeadCode = Third;
10447  }
10448  else if((FormatType == FNSNonRepeatToShuttle) || (FormatType == SNSNonRepeatFromShuttle))
10449  {
10450  if(CheckTimeValidity(9, First, ActionVectorEntry.EventTime))
10451  {
10452  ;
10453  }
10454  ActionVectorEntry.Command = Second;
10455  ActionVectorEntry.NonRepeatingShuttleLinkHeadCode = Third;
10456  }
10457  else if(FormatType == FSHNewService)
10458  {
10459  if(CheckTimeValidity(10, First, ActionVectorEntry.EventTime))
10460  {
10461  ;
10462  }
10463  ActionVectorEntry.Command = Second;
10464  ActionVectorEntry.OtherHeadCode = Third;
10465  ActionVectorEntry.NonRepeatingShuttleLinkHeadCode = Fourth;
10466  }
10467  else if(FormatType == FinRemHere)
10468  {
10469  ActionVectorEntry.Command = Second;
10470  }
10471  TempTrainDataEntry.ActionVector.push_back(ActionVectorEntry);
10472  }
10473  }
10474  else // last entry, & not entered under signaller control with no repeats, i.e. could be finish or repeat
10475  {
10476  OneEntry = NewRemainder;
10477  First = "";
10478  Second = "";
10479  Third = "";
10480  Fourth = "";
10481  RearStartOrRepeatMins = 0;
10482  FrontStartOrRepeatDigits = 0;
10483  NumberOfRepeats = 0;
10484  if((FinishFlag) && (OneEntry[1] != 'R'))
10485  // already had a finish entry
10486  {
10487  TimetableMessage(GiveMessages, "Error in timetable - Last event = '" + OneEntry + "'. An earlier finish event has been found with something other than a repeat following it - only a repeat can follow a finish event.");
10488  Utilities->CallLogPop(79);
10489  return(false);
10490  }
10491  if(OneEntry[1] != 'R') // must be finish
10492  {
10493  if(!SplitEntry(1, OneEntry, GiveMessages, CheckLocationsExistInRailway, First, Second, Third, Fourth, RearStartOrRepeatMins,
10494  FrontStartOrRepeatDigits, FormatType, LocationType, SequenceType, ShuttleLinkType, ExitList, Warning))
10495  {
10496  TimetableMessage(GiveMessages, "Error in timetable - Event: '" + OneEntry + "'");
10497  Utilities->CallLogPop(757);
10498  return(false);
10499  }
10500  if(SequenceType != Finish)
10501  {
10502  TimetableMessage(GiveMessages, "Error in timetable - last event should be a finish: '" + OneEntry + "'");
10503  Utilities->CallLogPop(785);
10504  return(false);
10505  }
10506  if(FinalCall)
10507  {
10508  // interpret & add to ActionVector
10509  TDateTime TempTime;
10510  TActionVectorEntry ActionVectorEntry;
10511  ActionVectorEntry.FormatType = FormatType;
10512  ActionVectorEntry.LocationType = LocationType;
10513  ActionVectorEntry.SequenceType = SequenceType;
10514  ActionVectorEntry.ShuttleLinkType = ShuttleLinkType;
10515  ActionVectorEntry.Warning = Warning;
10516  if(FormatType == TimeCmd)
10517  {
10518  if(CheckTimeValidity(11, First, ActionVectorEntry.EventTime))
10519  {
10520  ;
10521  }
10522  ActionVectorEntry.Command = Second;
10523  }
10524  else if(FormatType == TimeCmdHeadCode)
10525  {
10526  if(CheckTimeValidity(12, First, ActionVectorEntry.EventTime))
10527  {
10528  ;
10529  }
10530  ActionVectorEntry.Command = Second;
10531  ActionVectorEntry.OtherHeadCode = Third;
10532  }
10533  else if(FormatType == FNSNonRepeatToShuttle)
10534  {
10535  if(CheckTimeValidity(13, First, ActionVectorEntry.EventTime))
10536  {
10537  ;
10538  }
10539  ActionVectorEntry.Command = Second;
10540  ActionVectorEntry.NonRepeatingShuttleLinkHeadCode = Third;
10541  }
10542  else if(FormatType == FSHNewService)
10543  {
10544  if(CheckTimeValidity(14, First, ActionVectorEntry.EventTime))
10545  {
10546  ;
10547  }
10548  ActionVectorEntry.Command = Second;
10549  ActionVectorEntry.OtherHeadCode = Third;
10550  ActionVectorEntry.NonRepeatingShuttleLinkHeadCode = Fourth;
10551  }
10552  else if(FormatType == ExitRailway)
10553  {
10554  if(CheckTimeValidity(19, First, ActionVectorEntry.EventTime))
10555  {
10556  ;
10557  }
10558  ActionVectorEntry.Command = Second;
10559  ActionVectorEntry.ExitList = ExitList;
10560  }
10561  else if(FormatType == FinRemHere)
10562  {
10563  ActionVectorEntry.Command = Second;
10564  }
10565  TempTrainDataEntry.ActionVector.push_back(ActionVectorEntry);
10566  }
10567  }
10568  else // repeat
10569  {
10570  if(!SplitRepeat(0, OneEntry, RearStartOrRepeatMins, FrontStartOrRepeatDigits, NumberOfRepeats, GiveMessages))
10571  {
10572  Utilities->CallLogPop(786);
10573  // error messages given in SplitRepeat
10574  return(false);
10575  }
10576  if(FinalCall)
10577  {
10578  TActionVectorEntry ActionVectorEntry;
10579  ActionVectorEntry.FormatType = Repeat;
10580  ActionVectorEntry.LocationType = LocTypeForRepeatEntry;
10581  ActionVectorEntry.SequenceType = SequTypeForRepeatEntry;
10582  ActionVectorEntry.ShuttleLinkType = ShuttleLinkTypeForRepeatEntry;
10583  ActionVectorEntry.RearStartOrRepeatMins = RearStartOrRepeatMins;
10584  ActionVectorEntry.FrontStartOrRepeatDigits = FrontStartOrRepeatDigits;
10585  ActionVectorEntry.NumberOfRepeats = NumberOfRepeats;
10586  TempTrainDataEntry.ActionVector.push_back(ActionVectorEntry);
10587  }
10588  }
10589  }
10590  }
10591  if(FinalCall)
10592  {
10593  TrainDataVector.push_back(TempTrainDataEntry);
10594  }
10595  }
10596  Utilities->CallLogPop(80);
10597  return(true);
10598 }
10599 
10600 // ---------------------------------------------------------------------------
10601 
10602 bool TTrainController::Last2CharactersBothDigits(int Caller, AnsiString HeadCode)
10603 {
10604  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",Last2CharactersBothDigits," + HeadCode);
10605  if((HeadCode[HeadCode.Length() - 1] < '0') || (HeadCode[HeadCode.Length() - 1] > '9'))
10606  {
10607  Utilities->CallLogPop(1890);
10608  return(false);
10609  }
10610  if((HeadCode[HeadCode.Length()] < '0') || (HeadCode[HeadCode.Length()] > '9'))
10611  {
10612  Utilities->CallLogPop(1891);
10613  return(false);
10614  }
10615  Utilities->CallLogPop(1892);
10616  return(true);
10617 }
10618 
10619 // ---------------------------------------------------------------------------
10620 
10621 bool TTrainController::CheckTimeValidity(int Caller, AnsiString TimeStr, TDateTime &Time)
10622 // 1st 5 chars must be HH:MM, anything else will be ignored
10623 {
10624  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CheckTimeValidity," + TimeStr);
10625  if(TimeStr.Length() < 5)
10626  {
10627  Utilities->CallLogPop(926);
10628  return(false);
10629  }
10630  if((TimeStr[1] < '0') || (TimeStr[1] > '9'))
10631  {
10632  Utilities->CallLogPop(927);
10633  return(false);
10634  }
10635  if((TimeStr[2] < '0') || (TimeStr[2] > '9'))
10636  {
10637  Utilities->CallLogPop(928);
10638  return(false);
10639  }
10640  if(TimeStr[3] != ':')
10641  {
10642  Utilities->CallLogPop(929);
10643  return(false);
10644  }
10645  if((TimeStr[4] < '0') || (TimeStr[4] > '5'))
10646  {
10647  Utilities->CallLogPop(930);
10648  return(false);
10649  }
10650  if((TimeStr[5] < '0') || (TimeStr[5] > '9'))
10651  {
10652  Utilities->CallLogPop(931);
10653  return(false);
10654  }
10655  while(TimeStr.Length() > 5)
10656  {
10657  TimeStr = TimeStr.SubString(1, TimeStr.Length() - 1);
10658  }
10659  double WholeHours = (AnsiString(TimeStr[1]) + AnsiString(TimeStr[2])).ToDouble();
10660  double FracHour = ((AnsiString(TimeStr[4]) + AnsiString(TimeStr[5])).ToDouble()) / 60.0;
10661 
10662  if((WholeHours + FracHour) >= 95.98334)
10663  {
10664  Utilities->CallLogPop(1817);
10665  return(false); // > 95h 59m
10666  }
10667  Time = TDateTime((WholeHours + FracHour) / 24);
10668  Utilities->CallLogPop(932);
10669  return(true);
10670 }
10671 
10672 // ---------------------------------------------------------------------------
10673 
10674 bool TTrainController::SplitEntry(int Caller, AnsiString OneEntry, bool GiveMessages, bool CheckLocationsExistInRailway, AnsiString &First, AnsiString &Second,
10675  AnsiString &Third, AnsiString &Fourth, int &RearStartOrRepeatMins, int &FrontStartOrRepeatDigits, TTimetableFormatType &FormatType,
10676  TTimetableLocationType &LocationType, TTimetableSequenceType &SequenceType, TTimetableShuttleLinkType &ShuttleLinkType, TNumList &ExitList, bool &Warning)
10677 /* This is a train action entry from a single line of the timetable, i.e. not train information and not a repeat entry.
10678  Return false for failure.
10679  See description above under ProcessOneTimetableLinefor details of train action entries
10680  NB all types set except LocationType for Sns as may be located or not
10681 */{
10682  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SplitEntry," + OneEntry);
10683  Warning = false;
10684  TDateTime TempTime;
10685 
10686  if(OneEntry.Length() > 0)
10687  {
10688  if(OneEntry[1] == 'W') // warning
10689  {
10690  Warning = true;
10691  OneEntry = OneEntry.SubString(2, OneEntry.Length() - 1);
10692  // strip it off
10693  }
10694  }
10695  if(OneEntry == "Frh")
10696  {
10697  FormatType = FinRemHere;
10698  SequenceType = Finish;
10699  LocationType = AtLocation;
10700  ShuttleLinkType = NotAShuttleLink;
10701  Second = "Frh";
10702  Utilities->CallLogPop(1016);
10703  return(true);
10704  }
10705  if(OneEntry.Length() < 7)
10706  {
10707  Utilities->CallLogPop(907);
10708  return(false); // 'HH:MM;' + at least a one-letter location name
10709  }
10710  int Pos = OneEntry.Pos(';'); // first segment delimiter
10711 
10712  if(Pos != 6)
10713  {
10714  Utilities->CallLogPop(908);
10715  return(false);
10716  // no delimiter or delimiter not in position 6, has to be a time so fail
10717  }
10718  First = OneEntry.SubString(1, 5); // has to be a time
10719  if(!CheckTimeValidity(16, First, TempTime))
10720  {
10721  Utilities->CallLogPop(909);
10722  return(false);
10723  }
10724  AnsiString Remainder = OneEntry.SubString(Pos + 1, OneEntry.Length() - Pos);
10725 
10726  if((Remainder[1] >= '0') && (Remainder[1] <= '9'))
10727  // next segment is a time so this is a TimeTimeLoc & 3rd seg has to be a location to be valid
10728  {
10729  if(Remainder.Length() < 7)
10730  {
10731  Utilities->CallLogPop(910);
10732  return(false); // 'HH:MM;' + at least a one-letter location name
10733  }
10734  Pos = Remainder.Pos(';'); // second segment delimiter
10735  if(Pos == 0)
10736  {
10737  Utilities->CallLogPop(911);
10738  return(false);
10739  // no delimiter, has to be one between departure time & location
10740  }
10741  Second = Remainder.SubString(1, 5); // has to be a time
10742  if(!CheckTimeValidity(15, Second, TempTime))
10743  {
10744  Utilities->CallLogPop(912);
10745  return(false);
10746  }
10747  Third = Remainder.SubString(Pos + 1, Remainder.Length() - Pos);
10748  if(!CheckLocationValidity(0, Third, GiveMessages, CheckLocationsExistInRailway))
10749  {
10750  Utilities->CallLogPop(913);
10751  return(false);
10752  }
10753  FormatType = TimeTimeLoc;
10754  SequenceType = Intermediate;
10755  LocationType = AtLocation;
10756  ShuttleLinkType = NotAShuttleLink;
10757  Utilities->CallLogPop(914);
10758  return(true);
10759  }
10760  Pos = Remainder.Pos(';'); // second segment delimiter
10761  if(Pos == 0) // no third segment so second must be a location, or cdt
10762  {
10763  Second = Remainder;
10764  if(Second == "cdt")
10765  {
10766  FormatType = TimeCmd;
10767  ShuttleLinkType = NotAShuttleLink;
10768  LocationType = AtLocation;
10769  SequenceType = Intermediate;
10770  Utilities->CallLogPop(915);
10771  return(true);
10772  }
10773  if(!CheckLocationValidity(1, Second, GiveMessages, CheckLocationsExistInRailway))
10774  {
10775  Utilities->CallLogPop(916);
10776  return(false);
10777  }
10778  else
10779  {
10780  FormatType = TimeLoc;
10781  LocationType = AtLocation;
10782  SequenceType = Intermediate;
10783  ShuttleLinkType = NotAShuttleLink;
10784  Utilities->CallLogPop(917);
10785  return(true);
10786  }
10787  }
10788  // here if second segment is a command, with a third & maybe fourth segments as details
10789  if((Pos != 4) && (Pos != 7) && (Pos != 8))
10790  {
10791  Utilities->CallLogPop(918);
10792  return(false);
10793  // no third segement or not in position 4 or 7, & should be since all commands are 3, 6 or 7 letters
10794  }
10795  Second = Remainder.SubString(1, Pos - 1); // command
10796 
10797  Remainder = Remainder.SubString(Pos + 1, Remainder.Length() - Pos);
10798  // details
10799  Pos = Remainder.Pos(';'); // third segment delimiter
10800  if(Pos == 0)
10801  {
10802  Third = Remainder;
10803  }
10804  else
10805  {
10806  Third = Remainder.SubString(1, Pos - 1);
10807  Fourth = Remainder.SubString(Pos + 1, Remainder.Length() - Pos);
10808  }
10809  if((Second == "Snt") || (Second == "Snt-sh"))
10810  // third has to be 2 element idents with a space between
10811  {
10812  int SpacePos = Third.Pos(' ');
10813  if(SpacePos == 0)
10814  {
10815  Utilities->CallLogPop(919);
10816  return(false); // no space
10817  }
10818  AnsiString RearStartStr = Third.SubString(1, SpacePos - 1);
10819  AnsiString FrontStartStr = Third.SubString(SpacePos + 1, Third.Length() - SpacePos);
10820  // int RearPosition=0, FrontPosition=0, RearExitPos=0;
10821  if(CheckLocationsExistInRailway)
10822  {
10823  if(!CheckStartPositionValidity(0, RearStartStr, FrontStartStr, GiveMessages))
10824  {
10825  Utilities->CallLogPop(920);
10826  return(false);
10827  }
10828  RearStartOrRepeatMins = Track->GetTrackVectorPositionFromString(3, RearStartStr, GiveMessages);
10829  FrontStartOrRepeatDigits = Track->GetTrackVectorPositionFromString(4, FrontStartStr, GiveMessages);
10830  }
10831  if(Second == "Snt")
10832  {
10833  FormatType = StartNew;
10834  SequenceType = Start;
10835  LocationType = NoLocation;
10836  // can't be set until know whether located or not - done in SecondPassActions
10837  ShuttleLinkType = NotAShuttleLink;
10838  }
10839  else // Snt-sh
10840  {
10841  FormatType = SNTShuttle;
10842  LocationType = AtLocation;
10843  SequenceType = Start;
10844  ShuttleLinkType = ShuttleLink;
10845  if(!CheckHeadCodeValidity(0, GiveMessages, Fourth))
10846  {
10847  Utilities->CallLogPop(1038);
10848  return(false);
10849  }
10850  }
10851  Utilities->CallLogPop(921);
10852  return(true);
10853  }
10854  if(Second == "Sns-sh") // third & fourth have to be headcodes
10855  {
10856  FormatType = SNSShuttle;
10857  LocationType = AtLocation;
10858  SequenceType = Start;
10859  ShuttleLinkType = ShuttleLink;
10860  if(!CheckHeadCodeValidity(1, GiveMessages, Third))
10861  {
10862  Utilities->CallLogPop(1039);
10863  return(false);
10864  }
10865  if(!CheckHeadCodeValidity(2, GiveMessages, Fourth))
10866  {
10867  Utilities->CallLogPop(1040);
10868  return(false);
10869  }
10870  Utilities->CallLogPop(1041);
10871  return(true);
10872  }
10873  if(Second == "F-nshs")
10874  {
10875  FormatType = FNSNonRepeatToShuttle;
10876  LocationType = AtLocation;
10877  SequenceType = Finish;
10878  ShuttleLinkType = ShuttleLink;
10879  if(!CheckHeadCodeValidity(3, GiveMessages, Third))
10880  {
10881  Utilities->CallLogPop(1047);
10882  return(false);
10883  }
10884  Utilities->CallLogPop(1048);
10885  return(true);
10886  }
10887  if(Second == "Sns-fsh")
10888  {
10889  FormatType = SNSNonRepeatFromShuttle;
10890  LocationType = AtLocation;
10891  SequenceType = Start;
10892  ShuttleLinkType = ShuttleLink;
10893  if(!CheckHeadCodeValidity(4, GiveMessages, Third))
10894  {
10895  Utilities->CallLogPop(1098);
10896  return(false);
10897  }
10898  Utilities->CallLogPop(1099);
10899  return(true);
10900  }
10901  if(Second == "Fns-sh") // third & fourth have to be headcodes
10902  {
10903  FormatType = FSHNewService;
10904  LocationType = AtLocation;
10905  SequenceType = Finish;
10906  ShuttleLinkType = ShuttleLink;
10907  if(!CheckHeadCodeValidity(5, GiveMessages, Third))
10908  {
10909  Utilities->CallLogPop(1050);
10910  return(false);
10911  }
10912  if(!CheckHeadCodeValidity(6, GiveMessages, Fourth))
10913  {
10914  Utilities->CallLogPop(1051);
10915  return(false);
10916  }
10917  Utilities->CallLogPop(1052);
10918  return(true);
10919  }
10920  // new segment for 'pas'
10921  if(Second == "pas") // third has to be a location
10922  {
10923  FormatType = PassTime;
10924  LocationType = EnRoute;
10925  SequenceType = Intermediate;
10926  ShuttleLinkType = NotAShuttleLink;
10927  if(!CheckLocationValidity(2, Third, GiveMessages, CheckLocationsExistInRailway))
10928  {
10929  Utilities->CallLogPop(1515);
10930  return(false);
10931  }
10932  Utilities->CallLogPop(1516);
10933  return(true);
10934  }
10935  // new segment for revised 'Fer'
10936  if(Second == "Fer")
10937  // third has to be a set of IDs separated by spaces, and at least 1
10938  {
10939  FormatType = ExitRailway;
10940  LocationType = EnRoute;
10941  SequenceType = Finish;
10942  ShuttleLinkType = NotAShuttleLink;
10943  if(CheckLocationsExistInRailway)
10944  {
10945  if(!CheckAndPopulateListOfIDs(0, Third, ExitList, GiveMessages))
10946  {
10947  Utilities->CallLogPop(1519);
10948  return(false);
10949  }
10950  }
10951  Utilities->CallLogPop(1520);
10952  return(true);
10953  }
10954  // all remainder must be TimeCmdHeadCode types to be valid
10955  if((Second != "Fns") && (Second != "Fjo") && (Second != "jbo") && (Second != "fsp") && (Second != "rsp") && (Second != "Sfs") && (Second != "Sns") &&
10956  (Second != "Frh-sh"))
10957  {
10958  Utilities->CallLogPop(922);
10959  return(false); // all TimeCmdHeadCode types
10960  }
10961  if(!CheckHeadCodeValidity(7, GiveMessages, Third))
10962  {
10963  Utilities->CallLogPop(923);
10964  return(false);
10965  }
10966  FormatType = TimeCmdHeadCode;
10967  LocationType = AtLocation;
10968  if(Second == "Frh-sh")
10969  {
10970  ShuttleLinkType = ShuttleLink;
10971  }
10972  else
10973  {
10974  ShuttleLinkType = NotAShuttleLink;
10975  }
10976  if((Second == "Fns") || (Second == "Fjo") || (Second == "Frh-sh"))
10977  {
10978  SequenceType = Finish;
10979  }
10980  if((Second == "jbo") || (Second == "fsp") || (Second == "rsp"))
10981  {
10982  SequenceType = Intermediate;
10983  }
10984  if((Second == "Sfs") || (Second == "Sns"))
10985  {
10986  SequenceType = Start;
10987  }
10988  Utilities->CallLogPop(924);
10989  return(true);
10990 }
10991 
10992 // ---------------------------------------------------------------------------
10993 
10994 bool TTrainController::CheckLocationValidity(int Caller, AnsiString LocStr, bool GiveMessages, bool CheckLocationsExistInRailway)
10995 {
10996  // check that the location name exists in the railway (only if CheckLocationsExistInRailway is true), doesn't begin with a number
10997  // and contains no special characters
10998  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CheckLocationValidity," + LocStr);
10999  if(LocStr == "")
11000  {
11001  Utilities->CallLogPop(1353);
11002  return(false); // has to have at least one character
11003  }
11004  if((LocStr[1] >= '0') && (LocStr[1] <= '9'))
11005  {
11006  Utilities->CallLogPop(1354);
11007  return(false); // can't begin with a number
11008  }
11009  for(int x = 1; x < LocStr.Length() + 1; x++)
11010  {
11011  if(LocStr[x] < ' ')
11012  {
11013  Utilities->CallLogPop(1355);
11014  return(false); // contains a special character
11015  }
11016  if(LocStr[x] > 'z')
11017  {
11018  Utilities->CallLogPop(1356);
11019  return(false); // contains a character outside the standard ASCII set
11020  }
11021  }
11022  // check exists in railway location list if CheckLocationsExistInRailway is true
11023  if(CheckLocationsExistInRailway)
11024  {
11025  if(!Track->TimetabledLocationNameAllocated(3, LocStr))
11026  {
11027  TimetableMessage(GiveMessages, "Location name '" + LocStr +
11028  "' appears in the timetable but is not a valid name. To be valid the name must be a stopping location and apply to one or more platforms " +
11029  "(not concourses on their own), or to track at a blue non-station named location. BUT NOTE THAT trains can't stop at continuations so a name " +
11030  "that includes a continuation will not be valid.");
11031  Utilities->CallLogPop(1357);
11032  return(false);
11033  }
11034  }
11035  Utilities->CallLogPop(1358);
11036  return(true);
11037 }
11038 
11039 // ---------------------------------------------------------------------------
11040 
11041 bool TTrainController::CheckHeadCodeValidity(int Caller, bool GiveMessages, AnsiString HeadCode)
11042 {
11043  // if(!AnyHeadCodeValid) up to 8 characters total & last 4 characters must be NLNN where N = number and L = capital or small letter
11044  // if(AnyHeadCodeValid) up to 8 characters total, last 2 chars must be digits & last but 2 can be any alphanumeric, upper or lower case
11045  // NOTE: As of v0.6b AnyHeadCodeValid dropped, all headcodes are unrestricted
11046  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + "," + AnsiString((short)GiveMessages) + ",CheckHeadCodeValidity," +
11047  HeadCode);
11048  if((HeadCode.Length() < 4) || (HeadCode.Length() > 8))
11049  {
11050  TimetableMessage(GiveMessages, "Headcode error in '" + HeadCode +
11051  "', length must be between 4 and 8 characters, and last 4 must be a legitimate headcode. This error can also be caused by omitting a service reference after Sns, Snt-sh, Sns-sh, Fns, Fns-sh or Frh-sh");
11052  Utilities->CallLogPop(1359);
11053  return(false);
11054  }
11055  // firstly allow any printable character (ASCII >= CHAR(32) & <= CHAR(126)), as these allowed in 1st 4 characters
11056  for(int x = 1; x < (HeadCode.Length() + 1); x++)
11057  {
11058  if((HeadCode[x] < ' ') || (HeadCode[x] > '~'))
11059  {
11060  TimetableMessage(GiveMessages, "Non-printable character in headcode '" + HeadCode + "'");
11061  Utilities->CallLogPop(1895);
11062  return(false);
11063  }
11064  }
11065  // secondly ensure the true Headcode only has letters or digits
11066  for(int x = 3; x >= 0; x--)
11067  {
11068  if(((HeadCode[HeadCode.Length() - x] < 'A') || (HeadCode[HeadCode.Length() - x] > 'Z')) && ((HeadCode[HeadCode.Length() - x] < 'a') ||
11069  (HeadCode[HeadCode.Length() - x] > 'z')) && ((HeadCode[HeadCode.Length() - x] < '0') || (HeadCode[HeadCode.Length() - x] > '9')))
11070  {
11071  TimetableMessage(GiveMessages, "Headcode error in '" + HeadCode + "', headcode must consist of letters and digits only");
11072  Utilities->CallLogPop(1790);
11073  return(false);
11074  }
11075  }
11076  Utilities->CallLogPop(1364);
11077  return(true);
11078 }
11079 
11080 // ---------------------------------------------------------------------------
11081 
11082 bool TTrainController::CheckAndPopulateListOfIDs(int Caller, AnsiString IDSet, TNumList &ExitList, bool GiveMessages)
11083 // set of legitimate track element IDs, separated by spaces, and at least 1 present
11084 {
11085  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CheckSetOfIDs," + IDSet);
11086  ExitList.clear();
11087  AnsiString CurrentID = "";
11088 
11089  if(IDSet.Length() == 0)
11090  {
11091  TimetableMessage(GiveMessages, "Must have at least one exit element ID following 'Fer'");
11092  Utilities->CallLogPop(1521);
11093  return(false);
11094  }
11095  for(int x = 1; x <= IDSet.Length(); x++)
11096  {
11097  char C = IDSet[x];
11098  if(((C < '0') || (C > '9')) && (C != ' ') && (C != 'N') && (C != '-'))
11099  {
11100  TimetableMessage(GiveMessages, "Illegal character in the set of element IDs following 'Fer' in '" + IDSet + "'");
11101  Utilities->CallLogPop(1522);
11102  return(false);
11103  }
11104  }
11105  int Pos = IDSet.Pos(' '); // look for the first space
11106 
11107  while(true)
11108  {
11109  if(Pos == 0)
11110  {
11111  CurrentID = IDSet;
11112  IDSet = "";
11113  }
11114  else
11115  {
11116  CurrentID = IDSet.SubString(1, Pos - 1);
11117  IDSet = IDSet.SubString(Pos + 1, IDSet.Length() - Pos);
11118  }
11119  int VecPos = Track->GetTrackVectorPositionFromString(7, CurrentID, GiveMessages);
11120  if(VecPos == -1)
11121  {
11122  Utilities->CallLogPop(1523);
11123  return(false); // messages given in GetTrackVectorPositionFromString
11124  }
11125  else
11126  {
11127  if(Track->TrackElementAt(722, VecPos).TrackType != Continuation)
11128  {
11129  TimetableMessage(GiveMessages, "The element ID '" + CurrentID + "' following 'Fer' is not an exit");
11130  Utilities->CallLogPop(1524);
11131  return(false);
11132  }
11133  else
11134  {
11135  // first check for duplicates
11136  if(!ExitList.empty())
11137  {
11138  for(TNumListIterator ELIT = ExitList.begin(); ELIT != ExitList.end(); ELIT++)
11139  {
11140  if(*ELIT == VecPos)
11141  {
11142  TimetableMessage(GiveMessages, "The element ID '" + CurrentID + "' following 'Fer' duplicates an earlier element");
11143  Utilities->CallLogPop(1532);
11144  return(false);
11145  }
11146  }
11147  }
11148  // of OK add it to the list
11149  ExitList.push_back(VecPos);
11150  }
11151  }
11152  if(IDSet == "")
11153  {
11154  Utilities->CallLogPop(1525);
11155  return(true);
11156  }
11157  else
11158  {
11159  Pos = IDSet.Pos(' '); // look for the next space
11160  }
11161  } // while(true)
11162 }
11163 
11164 // ---------------------------------------------------------------------------
11165 bool TTrainController::SplitTrainInfo(int Caller, AnsiString TrainInfoStr, AnsiString &HeadCode, AnsiString &Description, int &StartSpeed, int &MaxRunningSpeed,
11166  int &Mass, double &MaxBrakeRate, double &PowerAtRail, int &SignallerSpeed, bool GiveMessages)
11167 // 7 or 8 items for a new train (6 or 7 semicolons), for a continuing service only need headcode, though can have a description, if other
11168 // data entered for continuing service then will be ignored - message given to warn user, checks appropriate number of items and validity
11169 // of each item
11170 {
11171  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SplitTrainInfo," + TrainInfoStr);
11172  int Pos = 0;
11173  AnsiString Remainder = "";
11174  int SemiColonCount = 0;
11175 
11176  for(int x = 1; x < TrainInfoStr.Length() + 1; x++)
11177  {
11178  if(TrainInfoStr[x] == ';')
11179  {
11180  SemiColonCount++;
11181  }
11182  }
11183  if((SemiColonCount != 6) && (SemiColonCount != 7) && (SemiColonCount != 1) && (SemiColonCount != 0))
11184  {
11185  TimetableMessage(GiveMessages, "Error in train information in '" + TrainInfoStr +
11186  "'. Should be headcode + optional description for a continuing service;" +
11187  " or headcode, description, start speed, max running speed, mass, brake force, power (and optional signaller max. speed) for a new service");
11188  Utilities->CallLogPop(880);
11189  return(false);
11190  }
11191  if(SemiColonCount == 0)
11192  {
11193  HeadCode = TrainInfoStr;
11194  if(!CheckHeadCodeValidity(8, GiveMessages, HeadCode))
11195  {
11196  Utilities->CallLogPop(881);
11197  return(false);
11198  }
11199  Utilities->CallLogPop(882);
11200  return(true);
11201  }
11202  if(SemiColonCount == 1) // headcode & description only
11203  {
11204  Pos = TrainInfoStr.Pos(';'); // 1st delimiter
11205  HeadCode = TrainInfoStr.SubString(1, Pos - 1);
11206  Description = TrainInfoStr.SubString(Pos + 1, TrainInfoStr.Length() - Pos);
11207  if(!CheckHeadCodeValidity(9, GiveMessages, HeadCode))
11208  {
11209  Utilities->CallLogPop(883);
11210  return(false);
11211  }
11212  if(Description == "")
11213  {
11214  TimetableMessage(GiveMessages, "Train description missing in '" + TrainInfoStr + "'");
11215  Utilities->CallLogPop(884);
11216  return(false);
11217  }
11218  if(Description.Length() > 60)
11219  {
11220  TimetableMessage(GiveMessages, "Train description too long, limit of 60 characters '" + TrainInfoStr + "'");
11221  Utilities->CallLogPop(1157);
11222  return(false);
11223  }
11224  for(int x = 1; x < Description.Length() + 1; x++)
11225  {
11226  if((Description[x] < ' ') || (Description[x] > '~'))
11227  {
11228  TimetableMessage(GiveMessages, "Train description contains invalid characters in '" + TrainInfoStr + "'");
11229  Utilities->CallLogPop(885);
11230  return(false);
11231  }
11232  }
11233  Utilities->CallLogPop(886);
11234  return(true);
11235  }
11236  // if here must have 6 or 7 semicolons
11237  Pos = TrainInfoStr.Pos(';'); // 1st delimiter
11238  HeadCode = TrainInfoStr.SubString(1, Pos - 1);
11239  Remainder = TrainInfoStr.SubString(Pos + 1, TrainInfoStr.Length() - Pos);
11240  if(!CheckHeadCodeValidity(10, GiveMessages, HeadCode))
11241  {
11242  Utilities->CallLogPop(887);
11243  return(false);
11244  }
11245  Pos = Remainder.Pos(';'); // 2nd delimiter
11246  Description = Remainder.SubString(1, Pos - 1);
11247  Remainder = Remainder.SubString(Pos + 1, Remainder.Length() - Pos);
11248  if(Description == "")
11249  {
11250  TimetableMessage(GiveMessages, "Train description missing in '" + TrainInfoStr + "'");
11251  Utilities->CallLogPop(888);
11252  return(false);
11253  }
11254  if(Description.Length() > 60)
11255  {
11256  TimetableMessage(GiveMessages, "Train description too long, limit of 60 characters '" + TrainInfoStr + "'");
11257  Utilities->CallLogPop(1158);
11258  return(false);
11259  }
11260  for(int x = 1; x < Description.Length() + 1; x++)
11261  {
11262  if((Description[x] < ' ') || (Description[x] > 126))
11263  {
11264  TimetableMessage(GiveMessages, "Train description contains invalid characters in '" + TrainInfoStr + "'");
11265  Utilities->CallLogPop(889);
11266  return(false);
11267  }
11268  }
11269  Pos = Remainder.Pos(';'); // 3rd delimiter
11270  AnsiString StartSpeedStr = Remainder.SubString(1, Pos - 1);
11271 
11272  Remainder = Remainder.SubString(Pos + 1, Remainder.Length() - Pos);
11273  if(StartSpeedStr == "")
11274  {
11275  TimetableMessage(GiveMessages, "Train starting speed missing in '" + TrainInfoStr + "'");
11276  Utilities->CallLogPop(890);
11277  return(false);
11278  }
11279  for(int x = 1; x < StartSpeedStr.Length() + 1; x++)
11280  {
11281  if((StartSpeedStr[x] < '0') || (StartSpeedStr[x] > '9'))
11282  {
11283  TimetableMessage(GiveMessages, "Train start speed contains invalid characters in '" + TrainInfoStr + "'");
11284  Utilities->CallLogPop(891);
11285  return(false);
11286  }
11287  }
11288  StartSpeed = StartSpeedStr.ToInt();
11289  if(StartSpeed > TTrain::MaximumSpeedLimit) // 400kph = 250mph
11290  {
11291  StartSpeed = TTrain::MaximumSpeedLimit;
11292  if(!SSHigh) // added at v2.4.0
11293  {
11294  TimetableMessage(GiveMessages, "Train starting speed > 400km/h in '" + TrainInfoStr + "'. Setting it to 400km/h");
11295  SSHigh = true;
11296  }
11297  }
11298  Pos = Remainder.Pos(';'); // 4th delimiter
11299  AnsiString MaxRunningSpeedStr = Remainder.SubString(1, Pos - 1);
11300 
11301  Remainder = Remainder.SubString(Pos + 1, Remainder.Length() - Pos);
11302  if(MaxRunningSpeedStr == "")
11303  {
11304  TimetableMessage(GiveMessages, "Train maximum running speed missing in '" + TrainInfoStr + "'");
11305  Utilities->CallLogPop(892);
11306  return(false);
11307  }
11308  for(int x = 1; x < MaxRunningSpeedStr.Length() + 1; x++)
11309  {
11310  if((MaxRunningSpeedStr[x] < '0') || (MaxRunningSpeedStr[x] > '9'))
11311  {
11312  TimetableMessage(GiveMessages, "Train maximum running speed contains invalid characters in '" + TrainInfoStr + "'");
11313  Utilities->CallLogPop(893);
11314  return(false);
11315  }
11316  }
11317  MaxRunningSpeed = MaxRunningSpeedStr.ToInt();
11318  if(MaxRunningSpeed > TTrain::MaximumSpeedLimit) // 400kph = 250mph
11319  {
11320  MaxRunningSpeed = TTrain::MaximumSpeedLimit;
11321  if(!MRSHigh) // added at v2.4.0
11322  {
11323  TimetableMessage(GiveMessages, "Train maximum running speed > 400km/h in '" + TrainInfoStr + "'. Setting it to 400km/h");
11324  MRSHigh = true;
11325  }
11326  }
11327  if(MaxRunningSpeed < 10)
11328  // changed at v0.6 to prevent low max speeds - can cause problems in SetTrainMovementValues
11329  {
11330  MaxRunningSpeed = 10;
11331  if(!MRSLow) // added at v2.4.0
11332  {
11333  TimetableMessage(GiveMessages, "Train maximum running speed can't be less than 10km/h in '" + TrainInfoStr + "', it will be set to 10km/h");
11334  MRSLow = true;
11335  }
11336  }
11337  Pos = Remainder.Pos(';'); // 5th delimiter
11338  AnsiString MassStr = Remainder.SubString(1, Pos - 1);
11339 
11340  Remainder = Remainder.SubString(Pos + 1, Remainder.Length() - Pos);
11341  if(MassStr == "")
11342  {
11343  TimetableMessage(GiveMessages, "Train mass missing in '" + TrainInfoStr + "'");
11344  Utilities->CallLogPop(895);
11345  return(false);
11346  }
11347  for(int x = 1; x < MassStr.Length() + 1; x++)
11348  {
11349  if((MassStr[x] < '0') || (MassStr[x] > '9'))
11350  {
11351  TimetableMessage(GiveMessages, "Train mass contains invalid characters in '" + TrainInfoStr + "'");
11352  Utilities->CallLogPop(896);
11353  return(false);
11354  }
11355  }
11356  Mass = MassStr.ToInt() * 1000; // convert tonnes to kg
11357  if(Mass > TTrain::MaximumMassLimit) // 10,000tonnes
11358  {
11359  Mass = TTrain::MaximumMassLimit;
11360  if(!MassHigh) // added at v2.4.0
11361  {
11362  TimetableMessage(GiveMessages, "Train mass > 10,000 tonnes in '" + TrainInfoStr + "'. Setting it to 10,000 tonnes");
11363  MassHigh = true;
11364  }
11365  }
11366  if(Mass == 0)
11367  {
11368  TimetableMessage(GiveMessages, "Train mass zero in '" + TrainInfoStr + "'");
11369  Utilities->CallLogPop(897);
11370  return(false);
11371  }
11372  Pos = Remainder.Pos(';'); // 6th delimiter
11373  AnsiString MaxBrakeForceStr = Remainder.SubString(1, Pos - 1);
11374 
11375  Remainder = Remainder.SubString(Pos + 1, Remainder.Length() - Pos);
11376  if(MaxBrakeForceStr == "")
11377  {
11378  TimetableMessage(GiveMessages, "Train braking force missing in '" + TrainInfoStr + "'");
11379  Utilities->CallLogPop(898);
11380  return(false);
11381  }
11382  for(int x = 1; x < (MaxBrakeForceStr.Length() + 1); x++)
11383  {
11384  if((MaxBrakeForceStr[x] != '.') && ((MaxBrakeForceStr[x] < '0') || (MaxBrakeForceStr[x] > '9')))
11385  {
11386  TimetableMessage(GiveMessages, "Train braking force contains invalid characters in '" + TrainInfoStr + "'");
11387  Utilities->CallLogPop(899);
11388  return(false);
11389  }
11390  }
11391  double MaxBrakeForce = MaxBrakeForceStr.ToDouble() * 1000;
11392 
11393  // convert to kg force
11394  if((MaxBrakeForce / Mass) > 1) // gives 'g' braking - 9.81m/s/s
11395  {
11396  MaxBrakeForce = Mass;
11397  if(!BFHigh) // added at v2.4.0
11398  {
11399  TimetableMessage(GiveMessages, "Train braking force too high in '" + TrainInfoStr + "'. Setting it to the same as the train mass");
11400  BFHigh = true;
11401  }
11402  }
11403  if((MaxBrakeForce / Mass) < 0.01)
11404  {
11405  MaxBrakeForce = Mass * 0.01;
11406  if(!BFLow) // added at v2.4.0
11407  {
11408  TimetableMessage(GiveMessages, "Train braking force too low in '" + TrainInfoStr + "'. Setting it to 1% of the train mass");
11409  BFLow = true;
11410  }
11411  }
11412  // convert to m/s/s
11413  MaxBrakeRate = MaxBrakeForce / Mass * 9.81;
11414  // now may have just a power entry or power and signaller max. speed
11415  AnsiString GrossPowerStr = "", SignallerSpeedStr = "";
11416 
11417  if(SemiColonCount == 6)
11418  {
11419  GrossPowerStr = Remainder;
11420  SignallerSpeedStr = "30"; // default value
11421  }
11422  else // must be 7
11423  {
11424  Pos = Remainder.Pos(';'); // 7th delimiter
11425  GrossPowerStr = Remainder.SubString(1, Pos - 1);
11426  SignallerSpeedStr = Remainder.SubString(Pos + 1, Remainder.Length() - Pos);
11427  }
11428  // deal with GrossPower
11429  if(GrossPowerStr == "")
11430  {
11431  TimetableMessage(GiveMessages, "Train power missing in '" + TrainInfoStr + "'");
11432  Utilities->CallLogPop(901);
11433  return(false);
11434  }
11435  for(int x = 1; x < GrossPowerStr.Length() + 1; x++)
11436  {
11437  if((GrossPowerStr[x] < '0') || (GrossPowerStr[x] > '9'))
11438  {
11439  TimetableMessage(GiveMessages, "Train power contains invalid characters in '" + TrainInfoStr + "'");
11440  Utilities->CallLogPop(902);
11441  return(false);
11442  }
11443  }
11444 
11445  double GrossPower = GrossPowerStr.ToInt() * 1000; // convert to W
11446 
11447  if(GrossPower > TTrain::MaximumPowerLimit) // 100MW
11448  {
11449  GrossPower = TTrain::MaximumPowerLimit;
11450  if(!PwrHigh)
11451  {
11452  TimetableMessage(GiveMessages, "Train power > 100,000kW in '" + TrainInfoStr + "'. Setting it to 100,000kW");
11453  PwrHigh = true;
11454  }
11455  }
11456  else if(GrossPower == 0) // changed at v2.4.0
11457  {
11458  GrossPower = 0.1;
11459  // can't be zero or AValue is zero and then have divide by zero error, so set to 0.1W so acceleration tiny (though should be intercepted before accel calculated)
11460  }
11461  else if((GrossPower > 0) && (GrossPower < 10000))
11462  // added at v2.4.0 to ensure min power of 8kW at rail unless zero (otherwise could have too low AValues
11463  {
11464  GrossPower = 10000;
11465  }
11466  PowerAtRail = GrossPower * 0.8;
11467  // apply ratio of 80% for rail to gross power (seems about average from an internet search)
11468 
11469  // deal with SignallerSpeed
11470  if(SignallerSpeedStr == "")
11471  {
11472  TimetableMessage(GiveMessages, "Signaller speed not set in '" + TrainInfoStr + "', either set a value or remove the extra semicolon");
11473  Utilities->CallLogPop(1771);
11474  return(false);
11475  }
11476  for(int x = 1; x < SignallerSpeedStr.Length() + 1; x++)
11477  {
11478  if((SignallerSpeedStr[x] < '0') || (SignallerSpeedStr[x] > '9'))
11479  {
11480  TimetableMessage(GiveMessages, "Signaller speed contains invalid characters in '" + TrainInfoStr + "'");
11481  Utilities->CallLogPop(1769);
11482  return(false);
11483  }
11484  }
11485  SignallerSpeed = SignallerSpeedStr.ToInt();
11486  if(SignallerSpeed > TTrain::MaximumSpeedLimit)
11487  {
11488  SignallerSpeed = TTrain::MaximumSpeedLimit;
11489  if(!SigSHigh)
11490  {
11491  TimetableMessage(GiveMessages, "Signaller speed > 400km/h in '" + TrainInfoStr + "'. Setting it to 400km/h");
11492  SigSHigh = true;
11493  }
11494  }
11495  if(SignallerSpeed < 10)
11496  // changed at v0.6 to prevent low max speeds - can cause problems in SetTrainMovementValues
11497  {
11498  SignallerSpeed = 10;
11499  if(!SigSLow)
11500  {
11501  TimetableMessage(GiveMessages, "Signaller speed can't be less than 10km/h in '" + TrainInfoStr + "', it will be set to 10km/h");
11502  SigSLow = true;
11503  }
11504  // Utilities->CallLogPop(1770);
11505  // return false;
11506  }
11507  Utilities->CallLogPop(904);
11508  return(true);
11509 }
11510 
11511 // ---------------------------------------------------------------------------
11512 
11513 bool TTrainController::SplitRepeat(int Caller, AnsiString OneEntry, int &RearStartOrRepeatMins, int &FrontStartOrRepeatDigits, int &NumberOfRepeats,
11514  bool GiveMessages)
11515 {
11516  // Format must be: R;mm;dd;nn mm may be 1, 2 or more digits, dd may be 1 or 2 digits, nn may be 1, 2 or more digits
11517  // function checks validity of each item and returns false for error
11518  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SplitRepeat," + OneEntry);
11519  if(OneEntry.Length() < 7)
11520  {
11521  TimetableMessage(GiveMessages, "Error in repeat: '" + OneEntry + "' - should be 'R;m;d;n'");
11522  Utilities->CallLogPop(865);
11523  return(false);
11524  }
11525  int SemiColonCount = 0;
11526 
11527  for(int x = 1; x < OneEntry.Length() + 1; x++)
11528  {
11529  if(OneEntry[x] == ';')
11530  {
11531  SemiColonCount++;
11532  }
11533  }
11534  if(SemiColonCount != 3)
11535  {
11536  TimetableMessage(GiveMessages, "Error in repeat: '" + OneEntry + "' - should be 'R;m;d;n'");
11537  Utilities->CallLogPop(866);
11538  return(false);
11539  }
11540  if((OneEntry[1] != 'R') || (OneEntry[2] != ';'))
11541  {
11542  TimetableMessage(GiveMessages, "Error in repeat: '" + OneEntry + "' - should be 'R;m;d;n'");
11543  Utilities->CallLogPop(867);
11544  return(false);
11545  }
11546  AnsiString Remainder = OneEntry.SubString(3, OneEntry.Length() - 2);
11547  // strip off R;
11548 
11549  int Pos = 0;
11550 
11551  Pos = Remainder.Pos(';');
11552  AnsiString MinutesStr = Remainder.SubString(1, Pos - 1);
11553 
11554  Remainder = Remainder.SubString(Pos + 1, Remainder.Length() - Pos);
11555  if(MinutesStr == "")
11556  {
11557  TimetableMessage(GiveMessages, "Error in repeat: '" + OneEntry + "' - minute increment segment missing");
11558  Utilities->CallLogPop(868);
11559  return(false);
11560  }
11561  if(MinutesStr.Length() > 3)
11562  // added for v2.3.1 following Albie Vowles' reported error in repeat value 03/02/20
11563  {
11564  TimetableMessage(GiveMessages, "Error in repeat: '" + OneEntry + "' - minute value too high, maximum value is 999");
11565  Utilities->CallLogPop(2119);
11566  return(false);
11567  }
11568  for(int x = 1; x < MinutesStr.Length() + 1; x++)
11569  {
11570  if((MinutesStr[x] < '0') || (MinutesStr[x] > '9'))
11571  {
11572  TimetableMessage(GiveMessages, "Error in repeat: '" + OneEntry + "' - non-digit character in minute increment segment");
11573  Utilities->CallLogPop(869);
11574  return(false);
11575  }
11576  }
11577  RearStartOrRepeatMins = MinutesStr.ToInt();
11578  if(RearStartOrRepeatMins == 0)
11579  {
11580  TimetableMessage(GiveMessages, "Repeat minute increment is zero in: '" + OneEntry + "' - can't have a zero value");
11581  Utilities->CallLogPop(870);
11582  return(false);
11583  }
11584  Pos = Remainder.Pos(';');
11585  AnsiString DigitsStr = Remainder.SubString(1, Pos - 1);
11586 
11587  Remainder = Remainder.SubString(Pos + 1, Remainder.Length() - Pos);
11588  if(DigitsStr == "")
11589  {
11590  TimetableMessage(GiveMessages, "Error in repeat: '" + OneEntry + "' - headcode increment segment missing");
11591  Utilities->CallLogPop(871);
11592  return(false);
11593  }
11594  for(int x = 1; x < DigitsStr.Length() + 1; x++)
11595  {
11596  if((DigitsStr[x] < '0') || (DigitsStr[x] > '9'))
11597  {
11598  TimetableMessage(GiveMessages, "Error in repeat: '" + OneEntry + "' - non-digit character in headcode increment segment");
11599  Utilities->CallLogPop(872);
11600  return(false);
11601  }
11602  }
11603  if(DigitsStr.Length() > 2)
11604  {
11605  TimetableMessage(GiveMessages, "Error in repeat: '" + OneEntry + "' - maximum number of digits for headcode increment is 2");
11606  Utilities->CallLogPop(873);
11607  return(false);
11608  }
11609  FrontStartOrRepeatDigits = DigitsStr.ToInt();
11610 /* allow zero digit increments so HC can stay same for repeated services - for many suburban services the headcode digits relate to the
11611  route rather than the service
11612  if(FrontStartOrRepeatDigits == 0)
11613  {
11614  TimetableMessage(GiveMessages, "Repeat headcode increment is zero in: '" + OneEntry + "' - can't have a zero value");
11615  Utilities->CallLogPop(874);
11616  return false;
11617  }
11618 */
11619  if(!Last2CharactersBothDigits(0, ServiceReference) && (FrontStartOrRepeatDigits > 0))
11620  // new for v0.6b for unrestricted headcodes
11621  {
11622  TimetableMessage(GiveMessages, "Error in repeat: '" + OneEntry +
11623  "' - a repeating service with incrementing digits must have digits as its last two headcode characters");
11624  Utilities->CallLogPop(1889);
11625  return(false);
11626  }
11627  AnsiString NumberStr = Remainder;
11628 
11629  if(NumberStr == "")
11630  {
11631  TimetableMessage(GiveMessages, "Error in repeat: '" + OneEntry + "' - number of repeats missing");
11632  Utilities->CallLogPop(875);
11633  return(false);
11634  }
11635  if(NumberStr.Length() > 4)
11636  // added for v2.3.1 following Albie Vowles' reported error 03/02/20
11637  {
11638  TimetableMessage(GiveMessages, "Error in repeat: '" + OneEntry + "' - repeat value too high, no timetabled event can exceed 95 hours & 59 minutes");
11639  Utilities->CallLogPop(2118);
11640  return(false);
11641  }
11642  for(int x = 1; x < NumberStr.Length() + 1; x++)
11643  {
11644  if((NumberStr[x] < '0') || (NumberStr[x] > '9'))
11645  // catches negative numbers
11646  {
11647  TimetableMessage(GiveMessages, "Error in repeat: '" + OneEntry + "' - non-digit character in number of repeats");
11648  Utilities->CallLogPop(876);
11649  return(false);
11650  }
11651  }
11652  NumberOfRepeats = NumberStr.ToInt();
11653  if(NumberOfRepeats == 0)
11654  {
11655  TimetableMessage(GiveMessages, "Number of repeats is zero in: '" + OneEntry + "' - if no repeats are needed the repeat should be omitted");
11656  Utilities->CallLogPop(877);
11657  return(false);
11658  }
11659  Utilities->CallLogPop(878);
11660  return(true);
11661 }
11662 
11663 // ---------------------------------------------------------------------------
11664 
11665 bool TTrainController::SecondPassActions(int Caller, bool GiveMessages, bool &TwoLocationFlag) //TwoLocationFlag added at v2.9.1
11666 /* Note that here the TrainDataVector has been compiled with FinalCall true in ProcessOneTimetableLine so work on the
11667  vector rather than the timetable
11668  Note also that for unlocated Snt entries the LocationType hasn't yet been set
11669 
11670  Many of the errors caught here duplicate those in the preliminary checks, but leave in for completeness
11671 
11672  For info:-
11673  class TActionVectorEntry //contains a single train action - repeat entry is also of this class though no train action is taken for it
11674  {
11675  public:
11676  AnsiString LocationName, Command, OtherHeadCode, NonRepeatingShuttleLinkHeadCode; ///< string values for timetabled action entries, null
11678  bool SignallerControl; ///< indicates a train that is defined by the timetable as under signaller control
11679  bool Warning; ///< if set triggers an alert in the warning panel when the action is reached
11680  int NumberOfRepeats; ///< the number of repeating services
11681  int RearStartOrRepeatMins, FrontStartOrRepeatDigits; ///< dual-purpose variables used for the TrackVectorPositions of the rear and front
11683  TDateTime EventTime, ArrivalTime, DepartureTime; ///< relevant times at which the action is timetabled, zeroed on creation so change
11685  TNumList ExitList; ///< the list of valid train exit TrackVector positions for 'Fer' entries (empty to begin with)
11686  TTimetableFormatType FormatType; ///< defines the timetable action type
11687  TTimetableLocationType LocationType; ///< indicates where the train is when the relevant action occurs
11688  TTimetableSequenceType SequenceType; ///< indicates where in the sequence of codes the action lies
11689  TTimetableShuttleLinkType ShuttleLinkType; ///< indicates whether or not the action relates to a shuttle service link
11690  TTrainDataEntry *LinkedTrainEntryPtr; ///< link pointer for use between fsp/rsp & Sfs; Fjo & jbo; Fns & Sns; & all shuttle to shuttle
11692  TTrainDataEntry *NonRepeatingShuttleLinkEntryPtr; ///< pointer used by shuttles for the non-shuttle train links, in & out, the
11694 
11695  // inline function
11696 
11698  TActionVectorEntry() {
11699  RearStartOrRepeatMins=0; FrontStartOrRepeatDigits=0; NumberOfRepeats=0; FormatType=NoFormat;
11700  SequenceType=NoSequence; LocationType=NoLocation; ShuttleLinkType=NoShuttleLink, EventTime=TDateTime(-1);
11701  ArrivalTime=TDateTime(-1); DepartureTime=TDateTime(-1); LinkedTrainEntryPtr=0; NonRepeatingShuttleLinkEntryPtr=0;
11702  Warning = false; SignallerControl = false;
11703  }
11704  };
11705 
11706  typedef std::vector<TActionVectorEntry> TActionVector;//contains all actions for a single train
11707 
11708  class TTrainDataEntry //contains all data for a single train - copied into train object when becomes active
11709  {
11710  public:
11711  AnsiString HeadCode, ServiceReference, Description; ///< headcode is the first train's headcode, rest are calculated from repeat
11714  double MaxBrakeRate; ///< in metres/sec/sec
11715  double MaxRunningSpeed; ///< in km/h
11716  double PowerAtRail; ///< in Watts (taken as 80% of the train's Gross Power, i.e. that entered by the user)
11717  int Mass; ///< in kg
11718  int NumberOfTrains; ///< number of repeats + 1
11719  int SignallerSpeed; ///< in km/h for use when under signaller control
11720  int StartSpeed; ///< in km/h
11721  TActionVector ActionVector; ///< all the actions for the train
11722  TTrainOperatingDataVector TrainOperatingDataVector; ///< operating information for the train including all its repeats
11723 
11724  //inline function
11725 
11727  TTrainDataEntry()
11728  {
11729  StartSpeed=0; MaxRunningSpeed=0; NumberOfTrains=0;
11730  }
11731  };
11732 
11733  Allowable successors:-
11734  Snt unlocated -> Fer, TimeLoc (arr), TimeTimeLoc, (new) pas; No others
11735  Snt located -> No starts, no finishes except Frh & Fjo (as of v2.0.0), no repeat, pas or TimeTimeLoc; any other cmd or TimeLoc (dep) OK
11736  Snt-sh -> No starts, finishes, repeats, pas or TimeTimeLoc; any other cmd or TimeLoc (dep) OK
11737  Sfs -> No starts, finishes, repeats, pas or TimeTimeLoc; any other cmd or TimeLoc (dep) OK (must have a TimeLoc departure somewhere in sequence to
11738  set location, else fails)
11739  Sns -> No starts, finishes, repeats, pas or TimeTimeLoc; any other cmd or TimeLoc (dep) OK (must have a TimeLoc departure somewhere in sequence to
11740  set location, else fails)
11741  Sns-sh -> No starts, finishes, repeats, pas or TimeTimeLoc; any other cmd or TimeLoc (dep) OK (must have a TimeLoc departure somewhere in sequence to
11742  set location, else fails)
11743  Sns-fsh -> No starts, finishes, repeats, pas or TimeTimeLoc; any other cmd or TimeLoc (dep) OK (must have a TimeLoc departure somewhere in sequence to
11744  set location, else fails)
11745  Fns -> R only
11746  F-nshs -> Nothing (no repeats permitted)
11747  Fjo -> R only
11748  Frh -> R only
11749  Fer -> R only
11750  Frh-sh -> R only
11751  Fns-sh -> R only
11752  jbo -> No starts, finishes, repeats, splits, pas or TimeTimeLoc; TimeLoc (dep), jbo or cdt OK
11753  fsp -> No starts, repeats, Fer, pas or TimeTimeLoc; TimeLoc (dep) or any other OK
11754  rsp -> No starts, repeats, Fer, pas or TimeTimeLoc; TimeLoc (dep) or any other OK
11755  cdt -> No starts, repeats, Fer, pas or TimeTimeLoc; TimeLoc (dep) or any other OK
11756  TimeLoc (arr) -> No starts, repeats, Fer, pas or TimeTimeLoc; TimeLoc (dep) or any other OK
11757  TimeLoc (dep) -> Fer, TimeLoc (arr), or TimeTimeLoc, (new) pas OK, no others
11758  TimeTimeLoc -> Fer, TimeLoc (arr), or TimeTimeLoc, (new) pas OK, no others
11759  (new) pas -> Fer, TimeLoc (arr), or TimeTimeLoc, (new) pas OK, no others
11760  Repeat -> Nothing
11761 
11762  There must be a TimeLoc arrival (or a Sns start at location) in a sequence so successive cmd locations can be set
11763  Check all Snt's & set Locations if located (located = zero start speed, either element at a location (but if rear element
11764  is a continuation then treated as unlocated), and location listed in the next TimeLoc entry, though needn't be immediately after)
11765  If Snt entry at a location specified in a following TimeLoc entry but start speed > 0 give error message
11766  Check all times increase or stay same through ActionVector
11767  Cycle through all entries in vector setting arr & dep times based on above list
11768  Add locations to all relevant cmd entries based on earlier arrival location (or earlier reference for Sfs & Sns)
11769  Check locations match the arr & dep TimeLoc entries
11770  Check same location doesn't appear twice before a cdt except for separate arr & dep TimeLocs
11771  Make above valid succession checks
11772  Check all splits have matching Sfs headcodes (both ways), add locations to SFSs & check times same
11773  Check all new service headcodes (Sns) have matching headcodes (both ways), add locations to SNHs & check times same
11774  Check a split to 'x' doesn't again split to 'x' (anywhere, not just for one train, since headcodes can be duplicated)
11775  Check each Fns has matching Sns headcodes (both ways), add locations to SNHs & check times same
11776  Check all joins have matching headcodes (both ways), locations & times & don't occur in same sequence
11777  Check each joined by train not joined by same train again (anywhere, not just for one train, since headcodes can be duplicated)
11778  Set train info for Sfs & Sns entries
11779  Check each repeat entry exactly matches any included joins or splits (user has to enter it to show that really wants it)
11780  Check at least one platform long enough for a split (only need 2 lengths) & disallow if not, need length of 2 & 1 extra
11781  element at each end, or length of 3 & 1 extra element at either end
11782  Check all TimeLocs have either Arr or Dep times set and EventTime == -1
11783  Check all Cmds have EventTime set & Arr & Dep times = -1
11784  Check all Sfs & Sns entries followed somewhere in sequence by a TimeLoc departure
11785  Check all locations except unlocated Snts, Fers and Repeats have a LocationName
11786 
11787  Give messages in function if errors detected and clear the vector. Return false for failure.
11788 */
11789 
11790 /* Earlier checks:-
11791  Checks carried out with error messages in this function:-
11792  At least one comma in the line (it's based on a csv file);
11793  No entries following train information;
11794  At least one comma in remainder after train information (i.e at least a start and a finish entry);
11795  SplitEntry returns false in an intermediate entry - message repeats the entry for information;
11796  First entry not a start entry;
11797  Train information incomplete before a start entry;
11798  Entry follows a finish entry but doesn't begin with 'R';
11799  SplitEntry returns false in a finish entry - message repeats the entry for information;
11800  Last action entry isn't a finish entry.
11801 
11802  Function returns false with no message if:-
11803  Timetable start time invalid (no message is given for an invalid time as the line is assumed to be an irrelevant line; if no start
11804  time is found at all then an error message is given in the calling function);
11805  SplitTrainInfo returns false (message given in called function);
11806  SplitRepeat returns false (message given in called function).
11807 
11808 Double crosslink (shuttle) table:
11809 
11810 Command Format OtherHead NonRepeating- LinkTrain- NonRepeating- Decsription
11811  Code ShuttleLink- EntryPtr ShuttleLink-
11812  HeadCode EntryPtr
11813 
11814 Snt-sh SNTShuttle Y (rtn shuttle) N Y (rtn sh) N Simple shuttle - no feeder service
11815 Frh-sh TimeCmdHeadCode Y (outwd shuttle) N Y (outwd sh) N Simple shuttle - no finishing service
11816 F-nshs FNSNonRepeatToShuttle N (shld be Y for outwd shuttle) Y (shld be N) Y (correct) N (correct) Feeder service link to shuttle
11817 Sns-sh SNSShuttle Y (rtn shuttle) Y (feeder) Y (rtn) Y (fdr) Shuttle link from feeder service
11818 Sns-fsh SNSNonRepeatFromShuttle N (shld be Y for rtn shuttle) Y (shld be N) Y (correct) N (correct) Finishing service link from shuttle
11819 Fns-sh FSHNewService Y (outwd shuttle) Y (finishing) Y (outwd sh) Y (finish) Shuttle link to finishing service
11820 
11821 Note: Any shuttle start can have any finish - feeder and finish, neither, feeder but no finish & vice versa.
11822 
11823 */{
11824  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SecondPassActions,");
11825  if(TrainDataVector.empty())
11826  {
11827  SecondPassMessage(GiveMessages, "Error in timetable - there appear to be no train services in the timetable, it must contain at least one");
11828  TrainDataVector.clear();
11829  Utilities->CallLogPop(1832);
11830  return(false);
11831  }
11832 /* new preliminary checks for v0.2b without changing anything, carry each out separately:-
11833  1) must have at least one actionvector entry
11834  2) if first actionvector entry not SignallerControl then must have at least one more actionvector entry
11835  3) if first actionvector entry is SignallerControl then must have no more actionvector entries except a repeat
11836  4) first entry must be a start;
11837  4a) if first entry is Snt and not signallercontrol and second is a finish then it can only be Frh, Fjo or Fer//added for v2.0.0
11838  5) a start must be the first entry;
11839  6) a repeat entry must be the last;
11840  7) for other than SignallerControl the last entry must be repeat or finish; if last entry is a repeat the last but one must be a finish;
11841  8) a finish entry must be the last or last but one, and if last but one the last must be a repeat
11842  Other successor errors will be caught later as all 'throws' changed to messages prior to the bulk of the sucessor checks
11843 */
11844 
11845  TwoLocationList.clear(); //empty the list to begin with, added at v2.9.1
11846  TwoLocationFlag = false; //added at v2.9.1
11847  for(unsigned int x = 0; x < TrainDataVector.size(); x++) // (1)
11848  {
11849  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
11850  if(TrainDataVector.at(x).ActionVector.empty())
11851  {
11852  SecondPassMessage(GiveMessages, "Error in timetable - the following service has no listed events, there must be at least one: " + TDEntry.HeadCode);
11853  TrainDataVector.clear();
11854  Utilities->CallLogPop(1833);
11855  return(false);
11856  }
11857  }
11858  for(unsigned int x = 0; x < TrainDataVector.size(); x++) // (2)
11859  {
11860  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
11861  TActionVectorEntry AVEntry0 = TrainDataVector.at(x).ActionVector.at(0);
11862  if(!(AVEntry0.SignallerControl))
11863  {
11864  if(TrainDataVector.at(x).ActionVector.size() == 1)
11865  {
11866  SecondPassMessage(GiveMessages, "Error in timetable - service must have a start event and at least one other for: " + TDEntry.HeadCode);
11867  TrainDataVector.clear();
11868  Utilities->CallLogPop(1822);
11869  return(false);
11870  }
11871  }
11872  }
11873  for(unsigned int x = 0; x < TrainDataVector.size(); x++) // (3)
11874  {
11875  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
11876  TActionVectorEntry AVEntry0 = TrainDataVector.at(x).ActionVector.at(0);
11877  if(AVEntry0.SignallerControl)
11878  {
11879  if(TrainDataVector.at(x).ActionVector.size() > 2)
11880  {
11881  SecondPassMessage(GiveMessages,
11882  "Error in timetable - a signaller control service can have no more than one item (a repeat) after the start event, see: " +
11883  TDEntry.HeadCode);
11884  TrainDataVector.clear();
11885  Utilities->CallLogPop(1837);
11886  return(false);
11887  }
11888  if(TrainDataVector.at(x).ActionVector.size() > 1)
11889  {
11890  TActionVectorEntry AVEntry1 = TrainDataVector.at(x).ActionVector.at(1);
11891  if(AVEntry1.FormatType != Repeat)
11892  {
11893  SecondPassMessage(GiveMessages,
11894  "Error in timetable - a signaller control service cannot have any other than a repeat after the start event, see: " + TDEntry.HeadCode);
11895  TrainDataVector.clear();
11896  Utilities->CallLogPop(1838);
11897  return(false);
11898  }
11899  }
11900  }
11901  }
11902  for(unsigned int x = 0; x < TrainDataVector.size(); x++) // (4)
11903  {
11904  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
11905  TActionVectorEntry AVEntry0 = TrainDataVector.at(x).ActionVector.at(0);
11906  if(AVEntry0.SequenceType != Start)
11907  {
11908  SecondPassMessage(GiveMessages, "Error in timetable - the first event must be a start for: " + TDEntry.HeadCode);
11909  TrainDataVector.clear();
11910  Utilities->CallLogPop(1824);
11911  return(false);
11912  }
11913  if((AVEntry0.Command == "Snt") && !(AVEntry0.SignallerControl))
11914  // 4a added at v2.0.0. This is only a rough check, Fer only valid for an unlocated Snt
11915  // and others for a located Snt, but those checks done later
11916  {
11917  TActionVectorEntry AVEntry1 = TrainDataVector.at(x).ActionVector.at(1);
11918  // must be a second entry if first not signallercontrol
11919  if((AVEntry1.SequenceType == Finish) && ((AVEntry1.Command == "Fns-sh") || (AVEntry1.Command == "Frh-sh")))
11920  {
11921  SecondPassMessage(GiveMessages, "Error in timetable - finish events Fns-sh and Frh-sh not permitted immediately after an Snt entry for: " +
11922  TDEntry.HeadCode);
11923  TrainDataVector.clear();
11924  Utilities->CallLogPop(2046);
11925  return(false);
11926  }
11927  }
11928  }
11929  for(unsigned int x = 0; x < TrainDataVector.size(); x++) // (5)
11930  {
11931  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
11932  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
11933  {
11934  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
11935  if((AVEntry.SequenceType == Start) && (y != 0))
11936  {
11937  SecondPassMessage(GiveMessages, "Error in timetable - a start event is present that is not the first event for: " + TDEntry.HeadCode);
11938  TrainDataVector.clear();
11939  Utilities->CallLogPop(1825);
11940  return(false);
11941  }
11942  }
11943  }
11944  for(unsigned int x = 0; x < TrainDataVector.size(); x++) // (6)
11945  {
11946  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
11947  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
11948  {
11949  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
11950  if((AVEntry.FormatType == Repeat) && (y != (TrainDataVector.at(x).ActionVector.size() - 1)))
11951  {
11952  SecondPassMessage(GiveMessages, "Error in timetable - a repeat is present that is not the last item for: " + TDEntry.HeadCode);
11953  TrainDataVector.clear();
11954  Utilities->CallLogPop(1826);
11955  return(false);
11956  }
11957  }
11958  }
11959  for(unsigned int x = 0; x < TrainDataVector.size(); x++) // (7)
11960  {
11961  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
11962  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
11963  {
11964  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
11965  if((y == 0) && AVEntry.SignallerControl)
11966  {
11967  break;
11968  }
11969  if(y == (TrainDataVector.at(x).ActionVector.size() - 1))
11970  {
11971  if((AVEntry.FormatType != Repeat) && (AVEntry.SequenceType != Finish))
11972  {
11973  SecondPassMessage(GiveMessages, "Error in timetable - the last item must be either a finish event or a repeat for: " + TDEntry.HeadCode);
11974  TrainDataVector.clear();
11975  Utilities->CallLogPop(1827);
11976  return(false);
11977  }
11978  if(AVEntry.FormatType == Repeat)
11979  {
11980  const TActionVectorEntry &LastAVEntry = TrainDataVector.at(x).ActionVector.at(y - 1);
11981  if(LastAVEntry.SequenceType != Finish)
11982  {
11983  SecondPassMessage(GiveMessages, "Error in timetable - the last event before the repeat must be a finish for: " + TDEntry.HeadCode);
11984  TrainDataVector.clear();
11985  Utilities->CallLogPop(1828);
11986  return(false);
11987  }
11988  }
11989  }
11990  }
11991  }
11992  for(unsigned int x = 0; x < TrainDataVector.size(); x++) // (8)
11993  {
11994  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
11995  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
11996  {
11997  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
11998  if(AVEntry.SequenceType == Finish)
11999  {
12000  if((y != (TrainDataVector.at(x).ActionVector.size() - 1)) && (y != (TrainDataVector.at(x).ActionVector.size() - 2)))
12001  {
12002  SecondPassMessage(GiveMessages, "Error in timetable - a finish event must be either the last or last but one for: " + TDEntry.HeadCode);
12003  TrainDataVector.clear();
12004  Utilities->CallLogPop(1829);
12005  return(false);
12006  }
12007  if(y == (TrainDataVector.at(x).ActionVector.size() - 2))
12008  {
12009  if(TrainDataVector.at(x).ActionVector.at(y + 1).FormatType != Repeat)
12010  {
12011  SecondPassMessage(GiveMessages, "Error in timetable - the only event that can follow a finish event is a repeat for: " +
12012  TDEntry.HeadCode);
12013  TrainDataVector.clear();
12014  Utilities->CallLogPop(1830);
12015  return(false);
12016  }
12017  }
12018  }
12019  }
12020  }
12021 
12022  // end of new preliminary checks
12023 
12024  // check ActionVector present and check start event successor validity
12025  // For Snt & Snt-sh set location if stopped, don't set any times yet
12026  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
12027  {
12028  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
12029  TActionVectorEntry & AVEntry0 = TrainDataVector.at(x).ActionVector.at(0);
12030  // use reference so can change internals where necessary
12031  if((AVEntry0.Command == "Snt") || (AVEntry0.Command == "Snt-sh"))
12032  {
12033  AnsiString LocationName = "";
12034  if(IsSNTEntryLocated(0, TDEntry, LocationName))
12035  // it is at a location
12036  {
12037  if(TDEntry.StartSpeed == 0) // stopped
12038  {
12039  AVEntry0.LocationName = LocationName;
12040  AVEntry0.LocationType = AtLocation;
12041  // check successor validity for located Snt that isn't a SignallerControl entry
12042  if(!AVEntry0.SignallerControl)
12043  {
12044  const TActionVectorEntry &AVEntry1 = TrainDataVector.at(x).ActionVector.at(1);
12045  // at least 2 entries present checked in integrity check so (1) valid
12046  if(!AtLocSuccessor(AVEntry1))
12047  {
12048  // Frh following Snt-sh will return false in location check, so no need to check here
12049  SecondPassMessage(GiveMessages, "Error in timetable - stopped 'Snt' or 'Snt-sh' followed by an illegal event for: " +
12050  TDEntry.HeadCode + ". The event isn't valid for a stationary train.");
12051  TrainDataVector.clear();
12052  Utilities->CallLogPop(523);
12053  return(false);
12054  }
12055  }
12056  }
12057  else
12058  {
12059  SecondPassMessage(GiveMessages, "Error in timetable - 'Snt' or 'Snt-sh' event at stop location but start speed not zero for: " +
12060  TDEntry.HeadCode);
12061  TrainDataVector.clear();
12062  Utilities->CallLogPop(791);
12063  return(false);
12064  }
12065  }
12066  else // check not Snt-sh & carry out successor validity checks for unlocated Snt that isn't a SignallerControl entry
12067  {
12068  if(AVEntry0.Command == "Snt-sh")
12069  {
12070  SecondPassMessage(GiveMessages, "Error in timetable - 'Snt-sh' event not at stop location for: " + TDEntry.HeadCode);
12071  TrainDataVector.clear();
12072  Utilities->CallLogPop(1042);
12073  return(false);
12074  }
12075  AVEntry0.LocationType = EnRoute;
12076  if(!AVEntry0.SignallerControl)
12077  {
12078  const TActionVectorEntry &AVEntry1 = TrainDataVector.at(x).ActionVector.at(1);
12079  // at least 2 entries checked in integrity check so (1) valid
12080  if(!MovingSuccessor(AVEntry1))
12081  {
12082  SecondPassMessage(GiveMessages, "Error in timetable - unlocated 'Snt' not followed by 'Fer', 'pas' or an arrival for: " +
12083  TDEntry.HeadCode);
12084  TrainDataVector.clear();
12085  Utilities->CallLogPop(790);
12086  return(false);
12087  }
12088  }
12089  }
12090  }
12091  // check other start successors
12092  else if(AVEntry0.SequenceType == Start)
12093  {
12094  const TActionVectorEntry &AVEntry1 = TrainDataVector.at(x).ActionVector.at(1);
12095  // at least 2 entries present checked in integrity check so (1) valid
12096  if(!AtLocSuccessor(AVEntry1))
12097  {
12098  SecondPassMessage(GiveMessages, "Error in timetable - 'Sfs', 'Sns', 'Sns-sh' or 'Sns-fsh' followed by an illegal event for: " +
12099  TDEntry.HeadCode + ". The event isn't valid for a stationary train.");
12100  TrainDataVector.clear();
12101  Utilities->CallLogPop(793);
12102  return(false);
12103  }
12104  }
12105  }
12106 
12107  // set Sfs, Sns, Sns-sh & 'Sns-fsh' locations same as following TimeLoc departure entry location, if no departure before end of sequence give error message
12108  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
12109  {
12110  bool FoundFlag = false;
12111  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
12112  TActionVectorEntry & AVEntry = TrainDataVector.at(x).ActionVector.at(0);
12113  // use reference so can change internals
12114  if((AVEntry.Command == "Sfs") || (AVEntry.Command == "Sns") || (AVEntry.Command == "Sns-sh") || (AVEntry.Command == "Sns-fsh"))
12115  {
12116  for(unsigned int y = 1; y < TrainDataVector.at(x).ActionVector.size(); y++)
12117  {
12118  const TActionVectorEntry &AVEntry2 = TrainDataVector.at(x).ActionVector.at(y);
12119  if(AVEntry2.FormatType == TimeLoc)
12120  {
12121  FoundFlag = true;
12122  AVEntry.LocationName = AVEntry2.LocationName;
12123  break;
12124  }
12125  }
12126  if(!FoundFlag)
12127  {
12128  SecondPassMessage(GiveMessages, "Error in timetable - no location departure following an 'Sfs', 'Sns', 'Sns-sh'or 'Sns-fsh' event for: " +
12129  TDEntry.HeadCode);
12130  TrainDataVector.clear();
12131  Utilities->CallLogPop(851);
12132  return(false);
12133  }
12134  }
12135  }
12136 
12137  // set all cmd locations based on earlier location name in TimeLoc arrival or Sfs/Sns/Sns-sh/Sns-fsh/located Snt/Snt-sh locations
12138  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
12139  {
12140  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
12141  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
12142  {
12143  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
12144  if((AVEntry.FormatType == TimeLoc) || ((AVEntry.SequenceType == Start) && (AVEntry.LocationType == AtLocation)))
12145  {
12146  if(AVEntry.LocationName == "")
12147  // if TimeLoc turns out to be a TimeLoc departure then will emerge & be rejected in successor checks for TimeLocs
12148  {
12149  SecondPassMessage(GiveMessages, "Error in timetable for " + TDEntry.HeadCode +
12150  ": an event should have had a location name associated with it but it could not be found");
12151  TrainDataVector.clear();
12152  Utilities->CallLogPop(1831);
12153  return(false);
12154  // throw Exception("Error, entry location null in TimeLoc/Sfs/Sns/Sns-sh/Sns-fsh/Snt-sh/located Snt for Train: " + TDEntry.HeadCode);
12155  }
12156  for(unsigned int z = y + 1; z < TrainDataVector.at(x).ActionVector.size(); z++)
12157  {
12158  TActionVectorEntry &AVEntry2 = TrainDataVector.at(x).ActionVector.at(z);
12159  // use reference so can change internals where necessary
12160  if((AVEntry2.Command != "") && (AVEntry2.LocationType == AtLocation))
12161  {
12162  AVEntry2.LocationName = AVEntry.LocationName;
12163  }
12164  else
12165  {
12166  break;
12167  }
12168  }
12169  }
12170  }
12171  }
12172  // all location names now set
12173 
12174  // check remaining successor validity except for TimeLoc arr & dep since those times not set yet
12175  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
12176  {
12177  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
12178  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
12179  {
12180  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
12181  if((AVEntry.SequenceType == Finish) && (AVEntry.Command != "F-nshs"))
12182  {
12183  if(y < (TrainDataVector.at(x).ActionVector.size() - 1))
12184  // i.e at least one more, must be a repeat
12185  {
12186  if(TrainDataVector.at(x).ActionVector.at(y + 1).FormatType != Repeat)
12187  {
12188  SecondPassMessage(GiveMessages, "Error in timetable - only a repeat can follow a finish entry for: " + TDEntry.HeadCode);
12189  TrainDataVector.clear();
12190  Utilities->CallLogPop(798);
12191  return(false);
12192  }
12193  }
12194  }
12195  if(AVEntry.Command == "F-nshs")
12196  {
12197  if(y != (TrainDataVector.at(x).ActionVector.size() - 1))
12198  // i.e has to be the last
12199  {
12200  SecondPassMessage(GiveMessages, "Error in timetable - F-nshs (shuttle link) must be the last event for: " + TDEntry.HeadCode);
12201  TrainDataVector.clear();
12202  Utilities->CallLogPop(1049);
12203  return(false);
12204  }
12205  }
12206  if(AVEntry.Command == "pas")
12207  {
12208  if(y >= (TrainDataVector.at(x).ActionVector.size() - 1))
12209  {
12210  SecondPassMessage(GiveMessages, "Error in timetable - a 'pas' can't be the last event for: " + TDEntry.HeadCode);
12211  TrainDataVector.clear();
12212  Utilities->CallLogPop(1518);
12213  return(false);
12214  }
12215  }
12216  if(AVEntry.Command == "jbo")
12217  {
12218  if(y >= (TrainDataVector.at(x).ActionVector.size() - 1))
12219  {
12220  SecondPassMessage(GiveMessages, "Error in timetable - a 'jbo' can't be the last event for: " + TDEntry.HeadCode);
12221  TrainDataVector.clear();
12222  Utilities->CallLogPop(800);
12223  return(false);
12224  }
12225  const TActionVectorEntry &AVEntry2 = TrainDataVector.at(x).ActionVector.at(y + 1);
12226  if(!AtLocSuccessor(AVEntry2))
12227  {
12228  SecondPassMessage(GiveMessages, "Error in timetable - a jbo event is followed by an illegal event for: " + TDEntry.HeadCode +
12229  ". The event isn't valid for a stationary train.");
12230  TrainDataVector.clear();
12231  Utilities->CallLogPop(801);
12232  return(false);
12233  }
12234  }
12235  if((AVEntry.Command == "fsp") || (AVEntry.Command == "rsp"))
12236  {
12237  if(y >= (TrainDataVector.at(x).ActionVector.size() - 1))
12238  {
12239  SecondPassMessage(GiveMessages, "Error in timetable - a train split can't be the last event for: " + TDEntry.HeadCode);
12240  TrainDataVector.clear();
12241  Utilities->CallLogPop(802);
12242  return(false);
12243  }
12244  const TActionVectorEntry &AVEntry2 = TrainDataVector.at(x).ActionVector.at(y + 1);
12245  if(!AtLocSuccessor(AVEntry2))
12246  {
12247  SecondPassMessage(GiveMessages, "Error in timetable - a train split is followed by an illegal event for: " + TDEntry.HeadCode +
12248  ". The event isn't valid for a stationary train.");
12249  TrainDataVector.clear();
12250  Utilities->CallLogPop(803);
12251  return(false);
12252  }
12253  }
12254  if(AVEntry.Command == "cdt")
12255  {
12256  if(y >= (TrainDataVector.at(x).ActionVector.size() - 1))
12257  {
12258  SecondPassMessage(GiveMessages, "Error in timetable - a 'cdt' can't be the last event for: " + TDEntry.HeadCode);
12259  TrainDataVector.clear();
12260  Utilities->CallLogPop(804);
12261  return(false);
12262  }
12263  const TActionVectorEntry &AVEntry2 = TrainDataVector.at(x).ActionVector.at(y + 1);
12264  if(!AtLocSuccessor(AVEntry2))
12265  {
12266  SecondPassMessage(GiveMessages, "Error in timetable - a 'cdt' is followed by an illegal event for: " + TDEntry.HeadCode +
12267  ". The event isn't valid for a stationary train.");
12268  TrainDataVector.clear();
12269  Utilities->CallLogPop(805);
12270  return(false);
12271  }
12272  }
12273  if(AVEntry.FormatType == TimeTimeLoc)
12274  {
12275  if(y >= (TrainDataVector.at(x).ActionVector.size() - 1))
12276  {
12277  SecondPassMessage(GiveMessages, "Error in timetable - a timed arrival and departure can't be the last event for: " + TDEntry.HeadCode);
12278  TrainDataVector.clear();
12279  Utilities->CallLogPop(806);
12280  return(false);
12281  }
12282  const TActionVectorEntry &AVEntry2 = TrainDataVector.at(x).ActionVector.at(y + 1);
12283  if(!MovingSuccessor(AVEntry2))
12284  {
12285  SecondPassMessage(GiveMessages, "Error in timetable - a timed arrival and departure is followed by an illegal event for: " +
12286  TDEntry.HeadCode + ". The event isn't valid for a moving train.");
12287  TrainDataVector.clear();
12288  Utilities->CallLogPop(807);
12289  return(false);
12290  }
12291  }
12292  if(AVEntry.FormatType == PassTime)
12293  {
12294  if(y >= (TrainDataVector.at(x).ActionVector.size() - 1))
12295  {
12296  SecondPassMessage(GiveMessages, "Error in timetable - a pass time can't be the last event for: " + TDEntry.HeadCode);
12297  TrainDataVector.clear();
12298  Utilities->CallLogPop(1530);
12299  return(false);
12300  }
12301  const TActionVectorEntry &AVEntry2 = TrainDataVector.at(x).ActionVector.at(y + 1);
12302  if(!MovingSuccessor(AVEntry2))
12303  {
12304  SecondPassMessage(GiveMessages, "Error in timetable - a pass time is followed by an illegal event for: " + TDEntry.HeadCode +
12305  ". The event isn't valid for a moving train.");
12306  TrainDataVector.clear();
12307  Utilities->CallLogPop(1531);
12308  return(false);
12309  }
12310  }
12311  if(AVEntry.FormatType == Repeat)
12312  {
12313  if(y != (TrainDataVector.at(x).ActionVector.size() - 1))
12314  {
12315  SecondPassMessage(GiveMessages, "Error in timetable - a repeat is not the last item for: " + TDEntry.HeadCode);
12316  TrainDataVector.clear();
12317  Utilities->CallLogPop(808);
12318  return(false);
12319  }
12320  }
12321  }
12322  }
12323 
12324  // set arrival & departure times for TimeLocs & set their EventTimes to -1
12325  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
12326  {
12327  bool LastEntryIsAnArrival = false;
12328  const TTrainDataEntry & TDEntry = TrainDataVector.at(x);
12329  // first deal with unlocated Snt entries - so next entry (TimeLoc or TimeTimeLoc) is an arrival, all else stopped so the next TimeLoc is a departure
12330  const TActionVectorEntry &AVEntry0 = TrainDataVector.at(x).ActionVector.at(0);
12331  if((AVEntry0.Command == "Snt") && (AVEntry0.LocationType == EnRoute))
12332  // StartSpeed may or may not be 0, but train will move forwards (if capable of doing so), & next TimeLoc will be an arrival, whether or not after one or more TimeTimeLocs
12333  {
12334  LastEntryIsAnArrival = false;
12335  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
12336  {
12337  TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
12338  if(AVEntry.FormatType == TimeLoc)
12339  {
12340  if((AVEntry.ArrivalTime > TDateTime(-1)) || (AVEntry.DepartureTime > TDateTime(-1)) || (AVEntry.EventTime == TDateTime(-1)))
12341  {
12342  throw Exception("Timetable error, TimeLoc times not as initially set for " + TDEntry.HeadCode);
12343  }
12344  if(LastEntryIsAnArrival)
12345  {
12346  AVEntry.DepartureTime = AVEntry.EventTime;
12347  AVEntry.EventTime = TDateTime(-1);
12348  LastEntryIsAnArrival = false;
12349  }
12350  else // last entry a departure
12351  {
12352  AVEntry.ArrivalTime = AVEntry.EventTime;
12353  AVEntry.EventTime = TDateTime(-1);
12354  LastEntryIsAnArrival = true;
12355  }
12356  }
12357  }
12358  }
12359  else // all others stopped at beginning
12360  {
12361  LastEntryIsAnArrival = true;
12362  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
12363  {
12364  TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
12365  if(AVEntry.FormatType == TimeLoc)
12366  {
12367  if((AVEntry.ArrivalTime > TDateTime(-1)) || (AVEntry.DepartureTime > TDateTime(-1)) || (AVEntry.EventTime == TDateTime(-1)))
12368  {
12369  throw Exception("Timetable error, TimeLoc times not as initially set for " + TDEntry.HeadCode);
12370  }
12371  if(LastEntryIsAnArrival)
12372  {
12373  AVEntry.DepartureTime = AVEntry.EventTime;
12374  AVEntry.EventTime = TDateTime(-1);
12375  LastEntryIsAnArrival = false;
12376  }
12377  else // last entry a departure
12378  {
12379  AVEntry.ArrivalTime = AVEntry.EventTime;
12380  AVEntry.EventTime = TDateTime(-1);
12381  LastEntryIsAnArrival = true;
12382  }
12383  }
12384  }
12385  }
12386  }
12387  // perform remaining successor checks for TimeLocs
12388  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
12389  {
12390  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
12391  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
12392  {
12393  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
12394  if((AVEntry.FormatType == TimeLoc) && (AVEntry.ArrivalTime >= TDateTime(0))) // arrival
12395  // TimeLoc (arr) -> No starts, repeats, Fer or TimeTimeLoc; TimeLoc (dep) or any other OK
12396  {
12397  if(y >= (TrainDataVector.at(x).ActionVector.size() - 1))
12398  {
12399  SecondPassMessage(GiveMessages, "Error in timetable - a timed arrival can't be the last event for: " + TDEntry.HeadCode);
12400  TrainDataVector.clear();
12401  Utilities->CallLogPop(809);
12402  return(false);
12403  }
12404  const TActionVectorEntry &AVEntry2 = TrainDataVector.at(x).ActionVector.at(y + 1);
12405  if(!AtLocSuccessor(AVEntry2))
12406  {
12407  SecondPassMessage(GiveMessages, "Error in timetable - a timed arrival is followed by an illegal event for: " + TDEntry.HeadCode +
12408  ". The event isn't valid for a stationary train.");
12409  TrainDataVector.clear();
12410  Utilities->CallLogPop(810);
12411  return(false);
12412  }
12413  }
12414  if((AVEntry.FormatType == TimeLoc) && (AVEntry.DepartureTime >= TDateTime(0))) // departure
12415  // TimeLoc (dep) -> Fer, TimeLoc (arr), TimeTimeLoc, (new) pas OK, no others
12416  {
12417  if(y >= (TrainDataVector.at(x).ActionVector.size() - 1))
12418  {
12419  SecondPassMessage(GiveMessages, "Error in timetable - a timed departure can't be the last event for: " + TDEntry.HeadCode);
12420  TrainDataVector.clear();
12421  Utilities->CallLogPop(811);
12422  return(false);
12423  }
12424  const TActionVectorEntry &AVEntry2 = TrainDataVector.at(x).ActionVector.at(y + 1);
12425  if(!MovingSuccessor(AVEntry2))
12426  {
12427  SecondPassMessage(GiveMessages, "Error in timetable - a timed departure is followed by an illegal event for: " + TDEntry.HeadCode +
12428  ". The event isn't valid for a moving train.");
12429  TrainDataVector.clear();
12430  Utilities->CallLogPop(812);
12431  return(false);
12432  }
12433  }
12434  }
12435  }
12436 
12437  // check all TimeLocs have either Arr or Dep time set and EventTime == -1, all Cmds have EventTime set & Arr & Dep times == -1,
12438  // & repeats have no times set
12439  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
12440  {
12441  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
12442  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
12443  {
12444  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
12445  if(AVEntry.FormatType == TimeLoc)
12446  {
12447  if(AVEntry.EventTime != TDateTime(-1))
12448  {
12449  throw Exception("Timetable error, TimeLoc entry has EventTime not -1 for " + TDEntry.HeadCode);
12450  }
12451  if((AVEntry.ArrivalTime == TDateTime(-1)) && (AVEntry.DepartureTime == TDateTime(-1)))
12452  {
12453  throw Exception("Timetable error, TimeLoc entry has neither arrival nor departure time set for " + TDEntry.HeadCode);
12454  }
12455  }
12456  if(AVEntry.FormatType == TimeTimeLoc)
12457  {
12458  if(AVEntry.EventTime != TDateTime(-1))
12459  {
12460  throw Exception("Timetable error, TimeTimeLoc entry has EventTime not -1 for " + TDEntry.HeadCode);
12461  }
12462  if((AVEntry.ArrivalTime == TDateTime(-1)) || (AVEntry.DepartureTime == TDateTime(-1)))
12463  {
12464  throw Exception("Timetable error, TimeTimeLoc entry has either arrival or departure time not set for " + TDEntry.HeadCode);
12465  }
12466  }
12467  if((AVEntry.FormatType == TimeCmd) || (AVEntry.FormatType == TimeCmdHeadCode) || (AVEntry.FormatType == StartNew) ||
12468  (AVEntry.FormatType == SNTShuttle) || (AVEntry.FormatType == SNSShuttle) || (AVEntry.FormatType == FNSNonRepeatToShuttle) ||
12469  (AVEntry.FormatType == FSHNewService) || (AVEntry.FormatType == PassTime))
12470  {
12471  if(AVEntry.EventTime == TDateTime(-1))
12472  {
12473  throw Exception("Timetable error, Cmd or PassTime entry has EventTime not set for " + TDEntry.HeadCode);
12474  }
12475  if((AVEntry.ArrivalTime != TDateTime(-1)) || (AVEntry.DepartureTime != TDateTime(-1)))
12476  {
12477  throw Exception("Timetable error, Cmd or PassTime entry has either arrival or departure time set for " + TDEntry.HeadCode);
12478  }
12479  }
12480  if(AVEntry.FormatType == Repeat)
12481  {
12482  if((AVEntry.EventTime != TDateTime(-1)) || (AVEntry.ArrivalTime != TDateTime(-1)) || (AVEntry.DepartureTime != TDateTime(-1)))
12483  {
12484  throw Exception("Timetable error, Repeat entry has a time set for " + TDEntry.HeadCode);
12485  }
12486  }
12487  }
12488  }
12489 
12490  // check times stay same or increase, note that can have time of 0 if include midnight
12491  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
12492  {
12493  TDateTime CurrentTime = TTClockTime; // the timetable start time
12494  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
12495  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
12496  {
12497  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
12498  if(AVEntry.FormatType == Repeat)
12499  {
12500  break;
12501  }
12502  if(AVEntry.FormatType == FinRemHere)
12503  {
12504  break;
12505  }
12506  if(AVEntry.FormatType == TimeTimeLoc)
12507  {
12508  if(AVEntry.DepartureTime < AVEntry.ArrivalTime)
12509  {
12510  SecondPassMessage(GiveMessages, "Error in timetable - a timed arrival and departure has a later arrival than departure time for: " +
12511  TDEntry.HeadCode);
12512  TrainDataVector.clear();
12513  Utilities->CallLogPop(813);
12514  return(false);
12515  }
12516  if(AVEntry.ArrivalTime < CurrentTime)
12517  {
12518  SecondPassMessage(GiveMessages, "Error in timetable - a timed arrival and departure has too early an arrival time for: " +
12519  TDEntry.HeadCode);
12520  TrainDataVector.clear();
12521  Utilities->CallLogPop(814);
12522  return(false);
12523  }
12524  CurrentTime = AVEntry.DepartureTime;
12525  continue;
12526  }
12527  if(AVEntry.FormatType == TimeLoc)
12528  {
12529  if(AVEntry.ArrivalTime >= TDateTime(0))
12530  {
12531  if(AVEntry.ArrivalTime < CurrentTime)
12532  {
12533  SecondPassMessage(GiveMessages, "Error in timetable - a timed location event has a time that is too early for: " + TDEntry.HeadCode);
12534  TrainDataVector.clear();
12535  Utilities->CallLogPop(815);
12536  return(false);
12537  }
12538  CurrentTime = AVEntry.ArrivalTime;
12539  }
12540  else
12541  {
12542  if(AVEntry.DepartureTime < CurrentTime)
12543  // both may be 0 legitimately so must allow for this
12544  {
12545  SecondPassMessage(GiveMessages, "Error in timetable - a timed location event has a time that is too early for: " + TDEntry.HeadCode);
12546  TrainDataVector.clear();
12547  Utilities->CallLogPop(816);
12548  return(false);
12549  }
12550  CurrentTime = AVEntry.DepartureTime;
12551  }
12552  continue;
12553  }
12554  if(AVEntry.EventTime < CurrentTime)
12555  // all others have EventTime set
12556  {
12557  SecondPassMessage(GiveMessages, "Error in timetable - a train event has a time that is set too early for: " + TDEntry.HeadCode +
12558  ", may be before timetable start time");
12559  TrainDataVector.clear();
12560  Utilities->CallLogPop(835);
12561  return(false);
12562  }
12563  CurrentTime = AVEntry.EventTime;
12564  continue;
12565  }
12566  }
12567 
12568  // check locations consistent
12569  AnsiString LastLocationName = "";
12570 
12571  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
12572  {
12573  bool LastEntryIsAnArrival = false;
12574  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
12575  // first deal with moving Snt entries (all else stopped)
12576  if((TrainDataVector.at(x).ActionVector.at(0).Command == "Snt") && (TrainDataVector.at(x).ActionVector.at(0).LocationType == EnRoute))
12577  {
12578  LastEntryIsAnArrival = false;
12579  LastLocationName = TrainDataVector.at(x).ActionVector.at(0).LocationName; // should be ""
12580  if(LastLocationName != "")
12581  {
12582  throw Exception("Timetable error, moving Snt entry has LocationName set for " + TDEntry.HeadCode);
12583  }
12584  for(unsigned int y = 1; y < TrainDataVector.at(x).ActionVector.size();
12585  y++) // note that immediate successor to a moving Snt can only be a Moving type
12586  {
12587  // if it's a SignallerControl entry then the condition isn't met
12588  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
12589  if(AVEntry.FormatType == Repeat)
12590  {
12591  break; // repeat = reached end (+allows repeat after signaller controlled entry)
12592  }
12593  else if((AVEntry.FormatType == TimeCmdHeadCode) || (AVEntry.FormatType == FNSNonRepeatToShuttle))
12594  {
12595  if(AVEntry.LocationName != LastLocationName)
12596  {
12597  SecondPassMessage(GiveMessages, "Error in timetable - a location event is inconsistent for: " + TDEntry.HeadCode + " && " +
12598  AVEntry.Command);
12599  TrainDataVector.clear();
12600  Utilities->CallLogPop(823);
12601  return(false);
12602  }
12603  }
12604  else if(AVEntry.FormatType == TimeCmd)
12605  // cdt is the only TimeCmd
12606  {
12607  if(AVEntry.LocationName != LastLocationName)
12608  {
12609  SecondPassMessage(GiveMessages, "Error in timetable - a location event is inconsistent for: " + TDEntry.HeadCode + " && " +
12610  AVEntry.Command);
12611  TrainDataVector.clear();
12612  Utilities->CallLogPop(824);
12613  return(false);
12614  }
12615  }
12616  else if(AVEntry.FormatType == TimeTimeLoc)
12617  {
12618  if((AVEntry.LocationName == LastLocationName) && TTEditPanelVisible) //changed at v2.6.0 to allow loops & consecutive same locs
12619  // last entry must be a departure or would have failed earlier
12620  {
12621  TwoLocationList.insert(TwoLocationList.end(), TDEntry.ServiceReference); //added at v2.9.1
12622  TwoLocationFlag = true;
12623 // ShowMessage("Two or more locations are the same without a change of direction between them. Please correct if this is an error.\n\nThis warning will not be shown again.");
12624 // TwoOrMoreLocationsWarningGiven = true;
12625  }
12626  LastLocationName = AVEntry.LocationName;
12627  LastEntryIsAnArrival = false;
12628  }
12629  else if(AVEntry.FormatType == TimeLoc)
12630  {
12631  if(LastEntryIsAnArrival && (AVEntry.LocationName != LastLocationName))
12632  {
12633  SecondPassMessage(GiveMessages,
12634  "Error in timetable - a location event for a timed departure is different from the arrival location for: " + TDEntry.HeadCode);
12635  TrainDataVector.clear();
12636  Utilities->CallLogPop(826);
12637  return(false);
12638  }
12639  else if(!LastEntryIsAnArrival && (AVEntry.LocationName == LastLocationName))
12640  {
12641  SecondPassMessage(GiveMessages,
12642  "Error in timetable - a location event for a timed arrival is the same as the earlier departure location for: " + TDEntry.HeadCode);
12643  TrainDataVector.clear();
12644  Utilities->CallLogPop(827);
12645  return(false);
12646  }
12647  LastLocationName = AVEntry.LocationName;
12648  LastEntryIsAnArrival = !LastEntryIsAnArrival;
12649  }
12650  }
12651  }
12652  else // all stationary starting entries
12653  {
12654  LastEntryIsAnArrival = true;
12655  LastLocationName = TrainDataVector.at(x).ActionVector.at(0).LocationName;
12656  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
12657  {
12658  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
12659  if(AVEntry.FormatType == Repeat)
12660  {
12661  break;
12662  }
12663  else if((AVEntry.FormatType == TimeCmdHeadCode) || (AVEntry.FormatType == FNSNonRepeatToShuttle))
12664  // no need to add anything for shuttle starts since they are at loc (0) anyway
12665  {
12666  if(AVEntry.LocationName != LastLocationName)
12667  {
12668  SecondPassMessage(GiveMessages, "Error in timetable - a location event is inconsistent for: " + TDEntry.HeadCode + " && " +
12669  AVEntry.Command);
12670  TrainDataVector.clear();
12671  Utilities->CallLogPop(828);
12672  return(false);
12673  }
12674  }
12675  else if(AVEntry.FormatType == TimeCmd)
12676  // cdt is the only TimeCmd
12677  {
12678  if(AVEntry.LocationName != LastLocationName)
12679  {
12680  SecondPassMessage(GiveMessages, "Error in timetable - a location event is inconsistent for: " + TDEntry.HeadCode + " && " +
12681  AVEntry.Command);
12682  TrainDataVector.clear();
12683  Utilities->CallLogPop(829);
12684  return(false);
12685  }
12686  }
12687  else if(AVEntry.FormatType == TimeTimeLoc)
12688  {
12689  if((AVEntry.LocationName == LastLocationName) && TTEditPanelVisible) //changed at v2.6.0 to allow loops & consecutive same locs
12690  // last entry must be a departure or would have failed earlier
12691  {
12692  TwoLocationList.insert(TwoLocationList.end(), TDEntry.ServiceReference); //added at v2.9.1
12693  TwoLocationFlag = true;
12694 // ShowMessage("Two or more locations are the same without a change of direction between them. Please correct if this is an error.\n\nThis warning will not be shown again.");
12695 // TwoOrMoreLocationsWarningGiven = true;
12696  }
12697  LastLocationName = AVEntry.LocationName;
12698  LastEntryIsAnArrival = false;
12699  }
12700  else if(AVEntry.FormatType == TimeLoc)
12701  {
12702  if(LastEntryIsAnArrival && (AVEntry.LocationName != LastLocationName))
12703  {
12704  SecondPassMessage(GiveMessages,
12705  "Error in timetable - a location event for a timed departure is different from the arrival location for: " + TDEntry.HeadCode);
12706  TrainDataVector.clear();
12707  Utilities->CallLogPop(831);
12708  return(false);
12709  }
12710  if(!LastEntryIsAnArrival && (AVEntry.LocationName == LastLocationName) && !TwoOrMoreLocationsWarningGiven)
12711  {
12712  SecondPassMessage(GiveMessages,
12713  "A location event for a timed arrival is the same as the earlier departure location for: " + TDEntry.HeadCode + ". Please correct if this is an error.\n\nThis warning will not be shown again.");
12715 // TrainDataVector.clear();
12716 // Utilities->CallLogPop(832);
12717 // return false;
12718  }
12719  LastLocationName = AVEntry.LocationName;
12720  LastEntryIsAnArrival = !LastEntryIsAnArrival;
12721  }
12722  }
12723  }
12724  }
12725 
12726  // Check same location doesn't appear twice before a cdt except for separate arr & dep TimeLocs (just a potential error warning given in v2.6.0)
12727  // i.e. same location can appear in any number of consecutive entries but once changed couldn't repeat before a direction change prior to v2.6.0
12728  AnsiString LocationNameToBeChecked = "";
12729 
12730  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
12731  {
12732  const TTrainDataEntry & TDEntry = TrainDataVector.at(x);
12733  unsigned int y = 0;
12734  const TActionVectorEntry &AVEntry0 = TDEntry.ActionVector.at(0);
12735  // first discard unlocated Snt entries as they don't have location name set
12736  if((AVEntry0.Command == "Snt") && (AVEntry0.LocationType == EnRoute))
12737  {
12738  y = 1;
12739  }
12740  while(y < TDEntry.ActionVector.size())
12741  // need to check each location name separately in turn, skipped for SignallerControl entries
12742  {
12743  if((TDEntry.ActionVector.at(y).Command == "Fer") || (TDEntry.ActionVector.at(y).FormatType == Repeat))
12744  {
12745  break; // out of the 'while' loop since have reached the end & 'Fer' & 'Repeat' have no location name set
12746  }
12747  LocationNameToBeChecked = TDEntry.ActionVector.at(y).LocationName;
12748  for(unsigned int z = y; z < TDEntry.ActionVector.size(); z++)
12749  {
12750  const TActionVectorEntry &AVEntry = TDEntry.ActionVector.at(z);
12751  if((AVEntry.Command == "Fer") || (AVEntry.FormatType == Repeat))
12752  {
12753  break; // out of the 'z' loop since have reached the end & 'Fer' & 'Repeat' have no location name set
12754  }
12755  if(AVEntry.Command == "cdt")
12756  {
12757  break; // out of the 'z' loop since the check is only valid up to a change of direction
12758  }
12759  if(AVEntry.LocationName == LocationNameToBeChecked)
12760  {
12761  continue; // keep going while name same
12762  }
12763  if(AVEntry.LocationName != LocationNameToBeChecked)
12764  // if name different check forwards to see if repeats
12765  {
12766  for(unsigned int a = z; a < TDEntry.ActionVector.size(); a++)
12767  {
12768  if(TDEntry.ActionVector.at(a).Command == "cdt")
12769  {
12770  break; // out of the 'a' & 'z' loops since the check is only valid up to a change of direction
12771  }
12772  if((TDEntry.ActionVector.at(a).LocationName == LocationNameToBeChecked) && TTEditPanelVisible) //changed at v2.6.0 to allow loops & consecutive same locs
12773  {
12774  TwoLocationList.insert(TwoLocationList.end(), TDEntry.ServiceReference); //added at v2.9.1
12775  TwoLocationFlag = true;
12776 // ShowMessage("Two or more locations are the same without a change of direction between them. Please correct if this is an error.\n\nThis warning will not be shown again.");
12777 // TwoOrMoreLocationsWarningGiven = true;
12778  }
12779  }
12780  break; // out of the 'z' loop since have checked 'a' as far as need to
12781  }
12782  }
12783  y++;
12784  }
12785  }
12786  if(TwoLocationFlag)
12787  {
12788  TwoLocationList.sort(); //need to sort first in alphabetical order to ensure all duplictes removed
12789  TwoLocationList.unique(); //remove duplicates
12790  }
12791 
12792  // check all locations except unlocated 'Snt' & 'Fer' have LocationName set
12793  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
12794  {
12795  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
12796  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
12797  {
12798  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
12799  if((AVEntry.LocationName == "") && (AVEntry.Command != "Snt") && (AVEntry.Command != "Fer") && (AVEntry.FormatType != Repeat))
12800  {
12801  throw Exception("Error, non- 'Snt', 'Fer' or Repeat entry doesn't have a location name set for " + TDEntry.HeadCode);
12802  }
12803  AnsiString LocName = "";
12804  // dummy, only used so can call IsSNTEntryLocated
12805  if((AVEntry.Command == "Snt") && (IsSNTEntryLocated(1, TrainDataVector.at(x), LocName)))
12806  {
12807  if(AVEntry.LocationName == "")
12808  {
12809  throw Exception("Error, 'Snt' entry at a stop location doesn't have a location name set for " + TDEntry.HeadCode);
12810  }
12811  }
12812  if((AVEntry.Command == "Snt") && !(IsSNTEntryLocated(2, TrainDataVector.at(x), LocName)))
12813  {
12814  if(AVEntry.LocationName != "")
12815  {
12816  throw Exception("Error, 'Snt' unlocated entry has a location name set for " + TDEntry.HeadCode);
12817  }
12818  }
12819  }
12820  }
12821 
12822 /* Check a split to 'x' doesn't again split to 'x' (anywhere, not just for one train, since headcodes can be duplicated)
12823  Check each joined by train not joined by same train again (anywhere, not just for one train, since headcodes can be duplicated)
12824  Check each change of headcode not repeated anywhere else (anywhere, not just for one train, since headcodes can be duplicated)
12825 
12826  i.e. check everywhere where there is an 'OtherHeadCode' that it matches once only with its reference (both ways) + set
12827  the OtherHeadCodeStartingEntryPtr pointers where appropriate + train information for splits & new services
12828 
12829  BUT need to separate the shuttles from non-shuttles, because can have two trains reference each other in both forms,
12830  eg 2F44 Sns-sh ends in Fns to 2F45, & Sns 2F45 ends in Fns-sh to 2F44. Here 2F45 is the 'OtherHeadCode' for both
12831  Sns-sh & Fns in train 2F44, & 2F44 is the 'OtherHeadCode' for both Sns & Fns-sh in train 2F45.
12832 */
12833  for(unsigned int x = 0; x < TrainDataVector.size(); x++) // new test to ensure no duplicate links at all, other checks ensure none for shuttles,
12834  {
12835  // non-shuttles & non-repeating links separately, but don't check that there isn't a
12836  // duplicate between a non-repeating shuttle and another - leave original tests in as
12837  // these also set the pointers
12838  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
12839  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
12840  {
12841  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
12842  if(AVEntry.OtherHeadCode != "")
12843  {
12844  if(!CheckForDuplicateCrossReferences(0, TDEntry.HeadCode, AVEntry.OtherHeadCode, GiveMessages))
12845  {
12846  Utilities->CallLogPop(1584);
12847  return(false); // error message given in called function
12848  }
12849  }
12850  if(AVEntry.NonRepeatingShuttleLinkHeadCode != "")
12851  {
12852  if(!CheckForDuplicateCrossReferences(1, TDEntry.HeadCode, AVEntry.NonRepeatingShuttleLinkHeadCode, GiveMessages))
12853  {
12854  Utilities->CallLogPop(1585);
12855  return(false); // error message given in called function
12856  }
12857  }
12858  }
12859  }
12860 
12861  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
12862  {
12863  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
12864  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
12865  {
12866  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
12867  if((AVEntry.Command != "Sns-sh") && (AVEntry.Command != "Snt-sh") && (AVEntry.Command != "Fns-sh") && (AVEntry.Command != "Frh-sh"))
12868  {
12869  if(AVEntry.OtherHeadCode != "")
12870  {
12871  if(!CheckCrossReferencesAndSetData(0, TDEntry.HeadCode, AVEntry.OtherHeadCode, false, GiveMessages))
12872  // false = non-shuttle
12873  {
12874  Utilities->CallLogPop(864);
12875  return(false); // error message given in called function
12876  }
12877  }
12878  }
12879  }
12880  }
12881 
12882  // now repeat the check just for the shuttles
12883  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
12884  {
12885  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
12886  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
12887  {
12888  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
12889  if((AVEntry.Command == "Sns-sh") || (AVEntry.Command == "Snt-sh") || (AVEntry.Command == "Fns-sh") || (AVEntry.Command == "Frh-sh"))
12890  {
12891  if(AVEntry.OtherHeadCode != "")
12892  {
12893  if(!CheckCrossReferencesAndSetData(1, TDEntry.HeadCode, AVEntry.OtherHeadCode, true, GiveMessages))
12894  // true = shuttle
12895  {
12896  Utilities->CallLogPop(1100);
12897  return(false); // error message given in called function
12898  }
12899  }
12900  }
12901  }
12902  }
12903 
12904  // check for proper non-repeating link cross references and that they have no repeats & that times are consistent
12905  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
12906  {
12907  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
12908  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
12909  {
12910  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
12911  if(AVEntry.NonRepeatingShuttleLinkHeadCode != "")
12912  {
12914  {
12915  Utilities->CallLogPop(1060);
12916  return(false); // error message given in called function
12917  }
12918  }
12919  }
12920  }
12921 
12922  // check that each shuttle start ends either in Fns or Fxx-sh (though a single service can't end in Fxx-sh), and that
12923  // when the Fxx-sh is reached it references the original start and not another shuttle - not allowed to link two shuttles,
12924  // don't ever need to and as designed would skip repeats
12925  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
12926  {
12927  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
12928  {
12929  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
12930  if((AVEntry.Command == "Sns-sh") || (AVEntry.Command == "Snt-sh"))
12931  {
12932  if(!CheckShuttleServiceIntegrity(0, &(TrainDataVector.at(x)), GiveMessages))
12933  {
12934  Utilities->CallLogPop(1090);
12935  return(false); // error message given in called function
12936  }
12937  }
12938  }
12939  }
12940 
12941  // check all entries have all types set to something
12942  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
12943  {
12944  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
12945  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
12946  {
12947  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
12948  if(AVEntry.FormatType == NoFormat)
12949  {
12950  throw Exception("Error - timetable ActionVector entry no. " + AnsiString(y) + " has FormatType unset for: " + TDEntry.HeadCode);
12951  }
12952  else if(AVEntry.SequenceType == NoSequence)
12953  {
12954  throw Exception("Error - timetable ActionVector entry no. " + AnsiString(y) + " has SequenceType unset for: " + TDEntry.HeadCode);
12955  }
12956  else if(AVEntry.LocationType == NoLocation)
12957  {
12958  throw Exception("Error - timetable ActionVector entry no. " + AnsiString(y) + " has LocationType unset for: " + TDEntry.HeadCode);
12959  }
12960  else if(AVEntry.ShuttleLinkType == NoShuttleLink)
12961  {
12962  throw Exception("Error - timetable ActionVector entry no. " + AnsiString(y) + " has ShuttleLinkType unset for: " + TDEntry.HeadCode);
12963  }
12964  }
12965  }
12966 
12967  // all OK if reach here, so set up the TrainOperatingDataVector (already has one entry) & NumberOfTrains
12968  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
12969  {
12970  TTrainDataEntry & TDEntry = TrainDataVector.at(x);
12971  // non-const reference so can alter content
12972  TTrainOperatingData TData;
12973  const TActionVectorEntry &LastAVEntry = TDEntry.ActionVector.at(TDEntry.ActionVector.size() - 1);
12974  if(LastAVEntry.FormatType == Repeat) // check if a repeat
12975  {
12976 /*
12977  class TTrainOperatingData
12978  {
12979  public:
12980  int TrainID; - default, set at construction
12981  TActionEventType EventReported; used during operation
12982  TRunningEntry RunningEntry; - default, set at construction
12983  TTrainOperatingData() {TrainID = -1; EventReported= NoEvent; RunningEntry=NotStarted;} //constructor, values set to defaults
12984  };
12985 */
12986  TDEntry.NumberOfTrains = LastAVEntry.NumberOfRepeats + 1;
12987  for(int y = 1; y < TDEntry.NumberOfTrains; y++)
12988  {
12989  TDEntry.TrainOperatingDataVector.push_back(TData);
12990  }
12991  }
12992  else
12993  {
12994  TDEntry.NumberOfTrains = 1;
12995  }
12996  }
12997 
12998  // check that don't include any Continuation names
12999  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
13000  {
13001  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
13002  {
13003  AnsiString LocName = TrainDataVector.at(x).ActionVector.at(y).LocationName;
13004  AnsiString HC = TrainDataVector.at(x).HeadCode;
13005  if(LocName != "")
13006  {
13007  if(Track->ContinuationNameMap.find(LocName) != Track->ContinuationNameMap.end())
13008  {
13009  SecondPassMessage(GiveMessages, "Error in timetable - continuation names (" + LocName + ") must not be included, see service " + HC);
13010  TrainDataVector.clear();
13011  Utilities->CallLogPop(1578);
13012  return(false);
13013  }
13014  }
13015  }
13016  }
13017 
13018  // check that all repeat times below 96h
13019  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
13020  {
13021  int NumRepeats = (TrainDataVector.at(x).NumberOfTrains) - 1;
13022  int IncMinutes = 0;
13023  if(TrainDataVector.at(x).ActionVector.at(TrainDataVector.at(x).ActionVector.size() - 1).FormatType == Repeat)
13024  {
13025  IncMinutes = TrainDataVector.at(x).ActionVector.at(TrainDataVector.at(x).ActionVector.size() - 1).RearStartOrRepeatMins;
13026  }
13027  else
13028  {
13029  continue; // basic times already checked in CheckTimeValidity
13030  }
13031  AnsiString HC = TrainDataVector.at(x).HeadCode;
13032  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
13033  {
13034  if((double)TrainDataVector.at(x).ActionVector.at(y).EventTime > -1)
13035  {
13036  if(((double)GetRepeatTime(32, TrainDataVector.at(x).ActionVector.at(y).EventTime, NumRepeats, IncMinutes) >= 3.9994))
13037  {
13038  SecondPassMessage(GiveMessages, "Error in timetable - a repeat time exceeds 95h 59m, see service " + HC); // 3d 23h 59m = 3.9993055556
13039  TrainDataVector.clear();
13040  Utilities->CallLogPop(1818);
13041  return(false);
13042  }
13043  }
13044  if((double)TrainDataVector.at(x).ActionVector.at(y).ArrivalTime > -1)
13045  {
13046  if(((double)GetRepeatTime(33, TrainDataVector.at(x).ActionVector.at(y).EventTime, NumRepeats, IncMinutes) >= 3.9994))
13047  // 3d 23h 59m = 3.9993055556
13048  {
13049  SecondPassMessage(GiveMessages, "Error in timetable - a repeat entry time exceeds 95h 59m, see service " + HC);
13050  TrainDataVector.clear();
13051  Utilities->CallLogPop(1819);
13052  return(false);
13053  }
13054  }
13055  if((double)TrainDataVector.at(x).ActionVector.at(y).DepartureTime > -1)
13056  {
13057  if(((double)GetRepeatTime(34, TrainDataVector.at(x).ActionVector.at(y).EventTime, NumRepeats, IncMinutes) >= 3.9994))
13058  // 3d 23h 59m = 3.9993055556
13059  {
13060  SecondPassMessage(GiveMessages, "Error in timetable - a repeat entry time exceeds 95h 59m, see service " + HC);
13061  TrainDataVector.clear();
13062  Utilities->CallLogPop(1820);
13063  return(false);
13064  }
13065  }
13066  }
13067  }
13068 
13069  // Now that all set up change any extended headcodes back to ordinary headcodes
13070  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
13071  {
13072  StripExcessFromHeadCode(0, TrainDataVector.at(x).HeadCode);
13073  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
13074  {
13075  StripExcessFromHeadCode(1, TrainDataVector.at(x).ActionVector.at(y).OtherHeadCode);
13076  StripExcessFromHeadCode(2, TrainDataVector.at(x).ActionVector.at(y).NonRepeatingShuttleLinkHeadCode);
13077  }
13078  }
13079 
13080  // SaveTrainDataVectorToFile(0);//test
13082  Utilities->CallLogPop(782);
13083  return(true);
13084 }
13085 
13086 // ---------------------------------------------------------------------------
13087 // Moving successors: TimeLoc arr/TimeTimeLoc/pas/Fer;
13089 {
13090  return ((AVEntry.FormatType == TimeLoc) || (AVEntry.FormatType == TimeTimeLoc) || (AVEntry.Command == "pas") || (AVEntry.Command == "Fer"));
13091 }
13092 
13093 // ---------------------------------------------------------------------------
13094 // AtLoc successors: TimeLoc dep/jbo/fsp/rsp/cdt/Frh/Fns/Fjo/Frh-sh/Fns-sh/F-nshs;
13096 {
13097  return ((AVEntry.FormatType == TimeLoc) || (AVEntry.Command == "jbo") || (AVEntry.Command == "fsp") || (AVEntry.Command == "rsp") ||
13098  (AVEntry.Command == "cdt") || (AVEntry.Command == "Frh") || (AVEntry.Command == "Fns") || (AVEntry.Command == "Fjo") || (AVEntry.Command == "Frh-sh") ||
13099  (AVEntry.Command == "Fns-sh") || (AVEntry.Command == "F-nshs"));
13100 }
13101 
13102 // ---------------------------------------------------------------------------
13103 
13104 void TTrainController::StripExcessFromHeadCode(int Caller, AnsiString &HeadCode)
13105 {
13106  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",StripExcessFromHeadCode," + HeadCode);
13107  if(HeadCode.Length() > 4) // ignore otherwise
13108  {
13109  HeadCode = HeadCode.SubString(HeadCode.Length() - 3, 4);
13110  }
13111  Utilities->CallLogPop(1593);
13112 }
13113 
13114 // ---------------------------------------------------------------------------
13115 
13116 bool TTrainController::CheckForDuplicateCrossReferences(int Caller, AnsiString MainHeadCode, AnsiString SecondHeadCode, bool GiveMessages)
13117 {
13118  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CheckForDuplicateCrossReferences," + MainHeadCode + "," +
13119  SecondHeadCode);
13120  int ForwardCount = 0;
13121  int ReverseCount = 0;
13122 
13123  if(MainHeadCode == SecondHeadCode)
13124  {
13125  SecondPassMessage(GiveMessages, "Error in timetable - Service " + MainHeadCode + " has an event that references itself");
13126  TrainDataVector.clear();
13127  Utilities->CallLogPop(1594);
13128  return(false);
13129  }
13130  // forward check
13131  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
13132  {
13133  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
13134  if(TDEntry.HeadCode == MainHeadCode)
13135  {
13136  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
13137  {
13138  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
13139  if(AVEntry.OtherHeadCode == SecondHeadCode)
13140  {
13141  ForwardCount++;
13142  }
13143  if(AVEntry.NonRepeatingShuttleLinkHeadCode == SecondHeadCode)
13144  // need own check in case both 'Other' & 'NonRepeating' have same headcode
13145  {
13146  ForwardCount++;
13147  }
13148  }
13149  }
13150  }
13151  if(ForwardCount == 0)
13152  // this is an exception because the headcodes are selected in the same order as the forward check
13153  {
13154  throw Exception("Error, ForwardCount == 0 in CheckForDuplicateCrossReferences after called with found values");
13155  }
13156  if(ForwardCount > 2)
13157  // can have 2 if one is Sns-sh linking from another leg of the shuttle, and Fns links out to that same leg
13158  {
13159  SecondPassMessage(GiveMessages, "Error in timetable - found more than two references to " + SecondHeadCode + " from a train whose headcode is " +
13160  MainHeadCode + ". Check the service cross references from each service, and check whether one or other service is listed twice or more.");
13161  TrainDataVector.clear();
13162  Utilities->CallLogPop(1587);
13163  return(false);
13164  }
13165  // reverse check
13166  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
13167  {
13168  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
13169  if(TDEntry.HeadCode == SecondHeadCode)
13170  {
13171  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
13172  {
13173  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
13174  if(AVEntry.OtherHeadCode == MainHeadCode)
13175  {
13176  ReverseCount++;
13177  }
13178  if(AVEntry.NonRepeatingShuttleLinkHeadCode == MainHeadCode)
13179  {
13180  ReverseCount++;
13181  }
13182  }
13183  }
13184  }
13185 
13186  if(ReverseCount == 0)
13187  {
13188  SecondPassMessage(GiveMessages, "Error in timetable - cross reference missing in either " + MainHeadCode + " or " + SecondHeadCode);
13189  TrainDataVector.clear();
13190  Utilities->CallLogPop(1588);
13191  return(false);
13192  }
13193  if(ReverseCount > 2)
13194  // can have 2 if one is a second shuttle leg with a link in from Fns, and it links out to the same service with Fxx-sh
13195  {
13196  SecondPassMessage(GiveMessages, "Error in timetable - found more than two references to " + MainHeadCode + " from a train whose headcode is " +
13197  SecondHeadCode + ". Check the service cross references from each service, and check whether one or other service is listed twice or more.");
13198  TrainDataVector.clear();
13199  Utilities->CallLogPop(1589);
13200  return(false);
13201  }
13202  if(ForwardCount != ReverseCount)
13203  {
13204  SecondPassMessage(GiveMessages, "Error in timetable - " + MainHeadCode + " has a different number of references to " + SecondHeadCode +
13205  " than the other way round");
13206  TrainDataVector.clear();
13207  Utilities->CallLogPop(1610);
13208  return(false);
13209  }
13210  Utilities->CallLogPop(1590);
13211  return(true);
13212 }
13213 
13214 // ---------------------------------------------------------------------------
13215 
13216 bool TTrainController::CheckCrossReferencesAndSetData(int Caller, AnsiString MainHeadCode, AnsiString OtherHeadCode, bool Shuttle, bool GiveMessages)
13217 /* Return false for no find or more than one find, check correct types of link
13218  First run through all trains whose headcode is the MainHeadCode (may be > 1) & for each entry whose
13219  'other' is OtherHeadCode increment a forward counter. Keep a pointer to the 'OtherHeadCode' entry for use later
13220  Must be exactly 1 forward count. NB Forward relates to MainHeadCode
13221  Then do the same in reverse.
13222  Using the pointers check the event times, then check that the locations & commands match - if main is a split then other must be Sfs;
13223  if main is Fns other must be Sns; if main is jbo other must be Fjo.
13224  Also check platform lengths OK for a split location (call to Track function for this - at least one platform at location has to be long
13225  enough). If all succeeds so far set the relevant OtherHeadCodeStartingEntryPtr to the new service starting point + train information
13226  for Sfs & Sns services. Finally check the repeat entries if present are consistent
13227 
13228  Check all except the NonRepeatingShuttleLinkHeadCodes, which only occur from F-nshs to Sns-sh, and from Fns-sh to
13229  Sns-fsh. All others should check out OK, but check shuttles & non-shuttles separately.
13230 
13231  /NB prohibit main & other headcodes being same, causes probs in failing to recognise locations
13232 */
13233 
13234 {
13235  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CheckCrossReferencesAndSetData," + MainHeadCode + "," + OtherHeadCode);
13236  int ForwardCount = 0;
13237  int ReverseCount = 0;
13238  unsigned int ForwardTDVectorNumber, ReverseTDVectorNumber;
13239  TActionVectorEntry *ReverseEntryPtr = 0, *ForwardEntryPtr = 0;
13240  TTrainDataEntry *MainTrainDataPtr = 0;
13241  TTrainDataEntry *OtherTrainDataPtr = 0;
13242 
13243  // forward check
13244  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
13245  {
13246  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
13247  if(TDEntry.HeadCode == MainHeadCode)
13248  {
13249  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
13250  {
13251  TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
13252  if(!Shuttle && (AVEntry.Command != "Sns-sh") && (AVEntry.Command != "Snt-sh") && (AVEntry.Command != "Fns-sh") && (AVEntry.Command != "Frh-sh"))
13253  {
13254  if(AVEntry.OtherHeadCode == OtherHeadCode)
13255  {
13256  MainTrainDataPtr = &TrainDataVector.at(x);
13257  ForwardEntryPtr = &AVEntry;
13258  ForwardCount++;
13259  ForwardTDVectorNumber = x;
13260  }
13261  }
13262  else if(Shuttle && ((AVEntry.Command == "Sns-sh") || (AVEntry.Command == "Snt-sh") || (AVEntry.Command == "Fns-sh") ||
13263  (AVEntry.Command == "Frh-sh")))
13264  {
13265  if(AVEntry.OtherHeadCode == OtherHeadCode)
13266  {
13267  MainTrainDataPtr = &TrainDataVector.at(x);
13268  ForwardEntryPtr = &AVEntry;
13269  ForwardCount++;
13270  ForwardTDVectorNumber = x;
13271  }
13272  }
13273  }
13274  }
13275  }
13276  if(ForwardCount == 0)
13277  // this is an exception because the headcodes are selected in the same order as the forward check
13278  {
13279  throw Exception("Error, ForwardCount == 0 in CheckCrossReferencesAndSetData after called with found values");
13280  }
13281  if(ForwardCount > 1)
13282  {
13283  SecondPassMessage(GiveMessages, "Error in timetable - found more than one reference to " + OtherHeadCode + " from a train whose headcode is " +
13284  MainHeadCode);
13285  TrainDataVector.clear();
13286  Utilities->CallLogPop(836);
13287  return(false);
13288  }
13289  // reverse check
13290  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
13291  {
13292  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
13293  if(TDEntry.HeadCode == OtherHeadCode)
13294  {
13295  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
13296  {
13297  TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
13298  if(!Shuttle && (AVEntry.Command != "Sns-sh") && (AVEntry.Command != "Snt-sh") && (AVEntry.Command != "Fns-sh") && (AVEntry.Command != "Frh-sh"))
13299  {
13300  if(AVEntry.OtherHeadCode == MainHeadCode)
13301  {
13302  OtherTrainDataPtr = &TrainDataVector.at(x);
13303  ReverseCount++;
13304  ReverseEntryPtr = &AVEntry;
13305  ReverseTDVectorNumber = x;
13306  }
13307  }
13308  else if(Shuttle && ((AVEntry.Command == "Sns-sh") || (AVEntry.Command == "Snt-sh") || (AVEntry.Command == "Fns-sh") || (AVEntry.Command == "Frh-sh")))
13309  {
13310  if(AVEntry.OtherHeadCode == MainHeadCode)
13311  {
13312  OtherTrainDataPtr = &TrainDataVector.at(x);
13313  ReverseCount++;
13314  ReverseEntryPtr = &AVEntry;
13315  ReverseTDVectorNumber = x;
13316  }
13317  }
13318  }
13319  }
13320  }
13321 
13322  if(ReverseCount == 0)
13323  {
13324  SecondPassMessage(GiveMessages, "Error in timetable - cross reference missing in either " + MainHeadCode + " or " + OtherHeadCode);
13325  TrainDataVector.clear();
13326  Utilities->CallLogPop(837);
13327  return(false);
13328  }
13329  if(ReverseCount > 1)
13330  {
13331  SecondPassMessage(GiveMessages, "Error in timetable - found more than one reference to " + MainHeadCode + " from a train whose headcode is " +
13332  OtherHeadCode);
13333  TrainDataVector.clear();
13334  Utilities->CallLogPop(838);
13335  return(false);
13336  }
13337  // these will all be false for !Shuttle
13338  bool ForwardShuttleStart = ((ForwardEntryPtr->Command == "Sns-sh") || (ForwardEntryPtr->Command == "Snt-sh"));
13339  bool ForwardShuttleFinish = ((ForwardEntryPtr->Command == "Fns-sh") || (ForwardEntryPtr->Command == "Frh-sh"));
13340  bool ReverseShuttleStart = ((ReverseEntryPtr->Command == "Sns-sh") || (ReverseEntryPtr->Command == "Snt-sh"));
13341  bool ReverseShuttleFinish = ((ReverseEntryPtr->Command == "Fns-sh") || (ReverseEntryPtr->Command == "Frh-sh"));
13342 
13343  if(Shuttle && MainTrainDataPtr->ActionVector.back().FormatType != Repeat)
13344  {
13345  SecondPassMessage(GiveMessages, "Error in timetable - shuttle train " + MainHeadCode + " does not have a repeat");
13346  TrainDataVector.clear();
13347  Utilities->CallLogPop(1058);
13348  return(false);
13349  }
13350  if(Shuttle && OtherTrainDataPtr->ActionVector.back().FormatType != Repeat)
13351  {
13352  SecondPassMessage(GiveMessages, "Error in timetable - shuttle train " + OtherHeadCode + " does not have a repeat");
13353  TrainDataVector.clear();
13354  Utilities->CallLogPop(1059);
13355  return(false);
13356  }
13357  if(ForwardEntryPtr->LocationName == "")
13358  {
13359  SecondPassMessage(GiveMessages, "Error in timetable - location error in cross referenced trains " + MainHeadCode + " and " + OtherHeadCode +
13360  ". One or other service does not have a location set");
13361  TrainDataVector.clear();
13362  Utilities->CallLogPop(526);
13363  return(false);
13364  }
13365  if(ReverseEntryPtr->LocationName == "")
13366  {
13367  SecondPassMessage(GiveMessages, "Error in timetable - location error in cross referenced trains " + MainHeadCode + " and " + OtherHeadCode +
13368  ". One or other service does not have a location set");
13369  TrainDataVector.clear();
13370  Utilities->CallLogPop(527);
13371  return(false);
13372  }
13373  if(ForwardEntryPtr->LocationName != ReverseEntryPtr->LocationName)
13374  {
13375  SecondPassMessage(GiveMessages, "Error in timetable - cross referenced train " + OtherHeadCode +
13376  " is at a different location to the referencing train " + MainHeadCode);
13377  TrainDataVector.clear();
13378  Utilities->CallLogPop(842);
13379  return(false);
13380  }
13381  // ignore shuttle repeat links for first time check
13382  if(!Shuttle)
13383  {
13384  if(ForwardEntryPtr->EventTime != ReverseEntryPtr->EventTime)
13385  {
13386  SecondPassMessage(GiveMessages, "Error in timetable - cross referenced train " + OtherHeadCode +
13387  " has a different event time to the referencing train " + MainHeadCode);
13388  TrainDataVector.clear();
13389  Utilities->CallLogPop(525);
13390  return(false);
13391  }
13392  }
13393  // need to allow for repeat times multiplying up by repeating time for shuttle repeat links
13394  // no need to check from reverse to forward as already checked links consistent, and if include will send message twice
13395  if(ForwardShuttleStart && ReverseShuttleFinish)
13396  // Shuttle must be true if these are true
13397  {
13398  if(!CheckShuttleRepeatTime(0, ForwardEntryPtr->EventTime, ReverseEntryPtr->EventTime, OtherTrainDataPtr->ActionVector.back().RearStartOrRepeatMins))
13399  {
13400  SecondPassMessage(GiveMessages, "Error in timetable - shuttle service " + MainHeadCode +
13401  " first repeat restart time not consistent with finish service " + OtherHeadCode);
13402  TrainDataVector.clear();
13403  Utilities->CallLogPop(1055);
13404  return(false);
13405  }
13406  }
13407  if((ReverseEntryPtr->Command == "Sfs") || (ReverseEntryPtr->Command == "Sns"))
13408  // doesn't matter about ForwardEntryPtr being Sfs/Sns as called for every occurrence of an 'OtherHeadCode' so won't escape
13409  {
13410  if(ReverseTDVectorNumber == ForwardTDVectorNumber)
13411  {
13412  SecondPassMessage(GiveMessages, "Error in timetable - an 'Sfs' or 'Sns' event (" + OtherHeadCode +
13413  ") appears in the same sequence as the corresponding linked event " + MainHeadCode);
13414  TrainDataVector.clear();
13415  Utilities->CallLogPop(528);
13416  return(false);
13417  }
13418  }
13419  if(ReverseEntryPtr->Command == "Fjo")
13420  // doesn't matter about ForwardEntryPtr being Fjo as called for every occurrence of an 'OtherHeadCode' so won't escape
13421  {
13422  if(ReverseTDVectorNumber == ForwardTDVectorNumber)
13423  {
13424  SecondPassMessage(GiveMessages, "Error in timetable - an 'Fjo' event (" + OtherHeadCode +
13425  ") appears in the same sequence as the corresponding linked event " + MainHeadCode);
13426  TrainDataVector.clear();
13427  Utilities->CallLogPop(862);
13428  return(false);
13429  }
13430  }
13431  if(ReverseEntryPtr->Command == "Fns")
13432  // doesn't matter about ForwardEntryPtr being Fns as called for every occurrence of an 'OtherHeadCode' so won't escape
13433  {
13434  if(ReverseTDVectorNumber == ForwardTDVectorNumber)
13435  {
13436  SecondPassMessage(GiveMessages, "Error in timetable - an 'Fns' event (" + OtherHeadCode +
13437  ") appears in the same sequence as the corresponding linked event " + MainHeadCode);
13438  TrainDataVector.clear();
13439  Utilities->CallLogPop(529);
13440  return(false);
13441  }
13442  }
13443  if(ForwardEntryPtr->Command == "Sfs")
13444  {
13445  if((ReverseEntryPtr->Command != "fsp") && (ReverseEntryPtr->Command != "rsp"))
13446  {
13447  SecondPassMessage(GiveMessages,
13448  "Error in timetable - unable to find a corresponding split train event for the train that starts from a split whose headcode is " +
13449  MainHeadCode);
13450  TrainDataVector.clear();
13451  Utilities->CallLogPop(530);
13452  return(false);
13453  }
13454  }
13455  if((ForwardEntryPtr->Command == "fsp") || (ForwardEntryPtr->Command == "rsp"))
13456  {
13457  if(ReverseEntryPtr->Command != "Sfs")
13458  {
13459  SecondPassMessage(GiveMessages, "Error in timetable - unable to find a corresponding 'Sfs' event for the train split whose headcode is " +
13460  MainHeadCode);
13461  TrainDataVector.clear();
13462  Utilities->CallLogPop(839);
13463  return(false);
13464  }
13465  else
13466  {
13467  if(!(Track->TimetabledLocationNameAllocated(4, ForwardEntryPtr->LocationName)))
13468  {
13469  SecondPassMessage(GiveMessages, "Error in timetable - can't find timetabled location '" + ForwardEntryPtr->LocationName + "' in railway - perhaps there are concourses without platforms?");
13470  TrainDataVector.clear();
13471  Utilities->CallLogPop(849);
13472  return(false);
13473  }
13474  if(!(Track->OneNamedLocationElementAtLocation(0, ForwardEntryPtr->LocationName)))
13475  {
13476  SecondPassMessage(GiveMessages, "Error in timetable - can't find any named location elements at '" + ForwardEntryPtr->LocationName + "' - perhaps there are concourses without platforms?");
13477  TrainDataVector.clear();
13478  Utilities->CallLogPop(850);
13479  return(false);
13480  }
13481  if(!(Track->OneNamedLocationLongEnoughForSplit(0, ForwardEntryPtr->LocationName)))
13482  {
13483  SecondPassMessage(GiveMessages, "Error in timetable - location too short to split a train at " + ForwardEntryPtr->LocationName);
13484  TrainDataVector.clear();
13485  Utilities->CallLogPop(846);
13486  return(false);
13487  }
13488  ForwardEntryPtr->LinkedTrainEntryPtr = OtherTrainDataPtr;
13489  ReverseEntryPtr->LinkedTrainEntryPtr = MainTrainDataPtr;
13490  if(OtherTrainDataPtr->Description == "")
13491  {
13492  OtherTrainDataPtr->Description = MainTrainDataPtr->Description;
13493  }
13494  // NB: May not be set if main train is a service continuation without a description, if so can't do much about it but doesn't affect operation, just the train information display
13495  OtherTrainDataPtr->MaxRunningSpeed = MainTrainDataPtr->MaxRunningSpeed;
13496  // Probably redundant as this is continued from the earlier service when the changeover happens (also may not be set here yet if a service continuation)
13497  }
13498  }
13499  if(ForwardEntryPtr->Command == "Sns")
13500  {
13501  if(ReverseEntryPtr->Command != "Fns")
13502  {
13503  SecondPassMessage(GiveMessages, "Error in timetable - unable to find a corresponding 'Fns' event for the 'Sns' train whose headcode is " +
13504  MainHeadCode + " and is formed from a service with headcode " + OtherHeadCode);
13505  TrainDataVector.clear();
13506  Utilities->CallLogPop(531);
13507  return(false);
13508  }
13509  }
13510  if(ForwardEntryPtr->Command == "Fns")
13511  {
13512  if(ReverseEntryPtr->Command != "Sns")
13513  {
13514  SecondPassMessage(GiveMessages, "Error in timetable - unable to find a corresponding 'Sns' event for the train whose headcode is " + MainHeadCode +
13515  " and forms a new service with headcode " + OtherHeadCode);
13516  TrainDataVector.clear();
13517  Utilities->CallLogPop(840);
13518  return(false);
13519  }
13520  else
13521  {
13522  ForwardEntryPtr->LinkedTrainEntryPtr = OtherTrainDataPtr;
13523  ReverseEntryPtr->LinkedTrainEntryPtr = MainTrainDataPtr;
13524  if(OtherTrainDataPtr->Description == "")
13525  {
13526  OtherTrainDataPtr->Description = MainTrainDataPtr->Description;
13527  }
13528  // Probably redundant as this is continued from the earlier service when the changeover happens (also may not be set here yet if a service continuation)
13529  OtherTrainDataPtr->MaxRunningSpeed = MainTrainDataPtr->MaxRunningSpeed;
13530  // Probably redundant as this is continued from the earlier service when the changeover happens (also may not be set here yet if a service continuation)
13531  }
13532  }
13533  if(ForwardEntryPtr->Command == "jbo")
13534  {
13535  if(ReverseEntryPtr->Command != "Fjo")
13536  {
13537  SecondPassMessage(GiveMessages, "Error in timetable - unable to find a corresponding 'Fjo' event for the train whose headcode is " + MainHeadCode +
13538  " and is joined by a train with headcode " + OtherHeadCode);
13539  TrainDataVector.clear();
13540  Utilities->CallLogPop(841);
13541  return(false);
13542  }
13543  }
13544  if(ForwardEntryPtr->Command == "Fjo")
13545  {
13546  if(ReverseEntryPtr->Command != "jbo")
13547  {
13548  SecondPassMessage(GiveMessages, "Error in timetable - unable to find a corresponding 'jbo' event for the train whose headcode is " + MainHeadCode +
13549  " and joins a train with headcode " + OtherHeadCode);
13550  TrainDataVector.clear();
13551  Utilities->CallLogPop(532);
13552  return(false);
13553  }
13554  else
13555  {
13556  ForwardEntryPtr->LinkedTrainEntryPtr = OtherTrainDataPtr;
13557  ReverseEntryPtr->LinkedTrainEntryPtr = MainTrainDataPtr;
13558  if((MainTrainDataPtr->MaxRunningSpeed > 5) && (MainTrainDataPtr->MaxRunningSpeed < OtherTrainDataPtr->MaxRunningSpeed))
13559  {
13560  OtherTrainDataPtr->MaxRunningSpeed = MainTrainDataPtr->MaxRunningSpeed;
13561  }
13562  // added test for > 5 [5 used instead of 0 because of possible floating point errors - though unlikely] above at v1.3.1 because the train will have a zero MaxRunningSpeed if it continues from another service - its max speed is set when it takes over from the other service
13563  // notified of this problem by Ian Walker in his email of 25/03/13. Probably redundant anyway because the max speed is reduced at the changeover if the 'joined by' train's max speed is less.
13564  }
13565  }
13566  if(ForwardShuttleStart)
13567  // (ForwardEntryPtr->Command == "Sns-sh") || (ForwardEntryPtr->Command == "Snt-sh"))
13568  {
13569  if(!ReverseShuttleFinish)
13570  // (ReverseEntryPtr->Command != "Fns-sh") && (ReverseEntryPtr->Command != "Frh-sh"))
13571  {
13572  SecondPassMessage(GiveMessages, "Error in timetable - incorrect shuttle link to train whose headcode is " + MainHeadCode +
13573  " from train whose headcode is " + OtherHeadCode + ", has to be Fns-sh, Frh-sh");
13574  TrainDataVector.clear();
13575  Utilities->CallLogPop(1056);
13576  return(false);
13577  }
13578  }
13579  if(ReverseShuttleStart)
13580  // (ReverseEntryPtr->Command == "Sns-sh") || (ReverseEntryPtr->Command == "Snt-sh"))
13581  {
13582  if(!ForwardShuttleFinish)
13583  // (ForwardEntryPtr->Command != "Fns-sh") && (ForwardEntryPtr->Command != "Frh-sh"))
13584  {
13585  SecondPassMessage(GiveMessages, "Error in timetable - incorrect shuttle link to train whose headcode is " + OtherHeadCode +
13586  " from train whose headcode is " + MainHeadCode + ", has to be Fns-sh, Frh-sh");
13587  TrainDataVector.clear();
13588  Utilities->CallLogPop(1057);
13589  return(false);
13590  }
13591  else
13592  {
13593  ForwardEntryPtr->LinkedTrainEntryPtr = OtherTrainDataPtr;
13594  ReverseEntryPtr->LinkedTrainEntryPtr = MainTrainDataPtr;
13595 /* don't need LinkedTrainEntryPtr for 'OtherTrain' & don't need data transfer as this is done in the
13596  non-repeating link for Sns-sh & is provided at the outset for Snt-sh
13597 */
13598  }
13599  }
13600  // check repeat information consistent if present
13601  // note that won't be affected by the non-repeating shuttle links as these are in NonRepeatingShuttleLinkHeadCode
13602  // and those not accessed here
13603 
13604  // still need to check the non-repeating links and that they have no repeats - do that outside this function
13605  bool MainRepeat = false, OtherRepeat = false;
13606  TActionVectorEntry MainRepeatEntry, OtherRepeatEntry;
13607 
13608  if(MainTrainDataPtr->ActionVector.at(MainTrainDataPtr->ActionVector.size() - 1).FormatType == Repeat)
13609  {
13610  MainRepeat = true;
13611  MainRepeatEntry = MainTrainDataPtr->ActionVector.at(MainTrainDataPtr->ActionVector.size() - 1);
13612  }
13613  if(OtherTrainDataPtr->ActionVector.at(OtherTrainDataPtr->ActionVector.size() - 1).FormatType == Repeat)
13614  {
13615  OtherRepeat = true;
13616  OtherRepeatEntry = OtherTrainDataPtr->ActionVector.at(OtherTrainDataPtr->ActionVector.size() - 1);
13617  }
13618  if((MainRepeat && !OtherRepeat) || (!MainRepeat && OtherRepeat))
13619  {
13620  SecondPassMessage(GiveMessages, "Error in timetable - only one repeat is provided for the train whose headcode is " + MainHeadCode +
13621  " and the associated train with headcode " + OtherHeadCode);
13622  TrainDataVector.clear();
13623  Utilities->CallLogPop(844);
13624  return(false);
13625  }
13626  if(MainRepeat && OtherRepeat)
13627  {
13628  if((MainRepeatEntry.EventTime != OtherRepeatEntry.EventTime) || (MainRepeatEntry.RearStartOrRepeatMins != OtherRepeatEntry.RearStartOrRepeatMins) ||
13629  (MainRepeatEntry.FrontStartOrRepeatDigits != OtherRepeatEntry.FrontStartOrRepeatDigits) ||
13630  (MainRepeatEntry.NumberOfRepeats != OtherRepeatEntry.NumberOfRepeats))
13631  {
13632  SecondPassMessage(GiveMessages, "Error in timetable - repeat items don't correspond for the train whose headcode is " + MainHeadCode +
13633  " and the associated train with headcode " + OtherHeadCode);
13634  TrainDataVector.clear();
13635  Utilities->CallLogPop(845);
13636  return(false);
13637  }
13638  }
13639  Utilities->CallLogPop(863);
13640  return(true);
13641 }
13642 
13643 // ---------------------------------------------------------------------------
13644 
13645 void TTrainController::StripSpaces(int Caller, AnsiString &Input)
13646 // strip both leading and trailing spaces at ends of text and spaces before and after all commas and semicolons within the text
13647 {
13648  // strip spaces from extreme ends of input
13649  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",StripSpaces," + AnsiString(Input));
13650  if(Input == "")
13651  {
13652  Utilities->CallLogPop(856);
13653  return;
13654  }
13655  while(Input[1] == ' ')
13656  {
13657  if(Input.Length() > 1)
13658  {
13659  Input = Input.SubString(2, Input.Length() - 1);
13660  }
13661  else
13662  {
13663  Input = "";
13664  Utilities->CallLogPop(857);
13665  return;
13666  }
13667  }
13668  if(Input == "")
13669  {
13670  Utilities->CallLogPop(858);
13671  return;
13672  }
13673  while(Input[Input.Length()] == ' ')
13674  {
13675  if(Input.Length() > 1)
13676  {
13677  Input = Input.SubString(1, Input.Length() - 1);
13678  }
13679  else
13680  {
13681  Input = "";
13682  Utilities->CallLogPop(859);
13683  return;
13684  }
13685  }
13686  // now strip spaces immediately after all commas and semicolons within the text
13687  AnsiString Output = "";
13688  bool DelimiterFound = false;
13689 
13690  for(int x = 1; x < Input.Length() + 1; x++)
13691  {
13692  if(DelimiterFound)
13693  {
13694  if(Input[x] == ' ')
13695  {
13696  continue;
13697  }
13698  }
13699  if((Input[x] != ',') && (Input[x] != ';'))
13700  {
13701  DelimiterFound = false;
13702  Output = Output + Input[x];
13703  }
13704  else
13705  {
13706  DelimiterFound = true;
13707  Output = Output + Input[x];
13708  }
13709  }
13710  if(Output == "")
13711  {
13712  Input = "";
13713  Utilities->CallLogPop(860);
13714  return;
13715  }
13716  // now strip spaces immediately before all commas and semicolons within the text
13717  Input = Output;
13718  Output = "";
13719  DelimiterFound = false;
13720  for(int x = Input.Length(); x > 0; x--)
13721  {
13722  if(DelimiterFound)
13723  {
13724  if(Input[x] == ' ')
13725  {
13726  continue;
13727  }
13728  }
13729  if((Input[x] != ',') && (Input[x] != ';'))
13730  {
13731  DelimiterFound = false;
13732  Output = AnsiString(Input[x]) + Output;
13733  }
13734  else
13735  {
13736  DelimiterFound = true;
13737  Output = AnsiString(Input[x]) + Output;
13738  }
13739  }
13740  Input = Output;
13741  Utilities->CallLogPop(861);
13742 }
13743 
13744 // ---------------------------------------------------------------------------
13745 
13746 bool TTrainController::IsSNTEntryLocated(int Caller, const TTrainDataEntry &TDEntry, AnsiString &LocationName)
13747 // checks if an Snt or Snt-sh entry followed (somewhere, not necessarily immediately) by a TimeLoc has the same LocationName
13748 // and if so returns true. Also returns true for Snt, not Snt-sh, if at least 1 start element is a location & the entry is either
13749 // a signaller control entry & speed is zero or it is followed immediately by Frh or Fjo (mod at v2.0.0 for empty stock pickup).
13750 // Always return false for entry at a continuation (may be named but not a stop location). Note that no successor validity checks
13751 // are done in this function, they must be done elsewhere.
13752 {
13753  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",IsSNTEntryLocated," + AnsiString(TDEntry.HeadCode));
13754  const TActionVectorEntry &AVEntry0 = TDEntry.ActionVector.at(0);
13755 
13756  LocationName = "";
13757  if((AVEntry0.Command != "Snt") && (AVEntry0.Command != "Snt-sh"))
13758  {
13759  throw Exception("Error, first entry not 'Snt' or 'Snt-sh' in IsSNTEntryLocated");
13760  }
13762  {
13763  Utilities->CallLogPop(852);
13764  return(false);
13765  }
13766  AnsiString LocRear = Track->TrackElementAt(507, AVEntry0.RearStartOrRepeatMins).ActiveTrackElementName;
13767  AnsiString LocFront = Track->TrackElementAt(508, AVEntry0.FrontStartOrRepeatDigits).ActiveTrackElementName;
13768 
13769  if(LocRear != "")
13770  {
13771  LocationName = LocRear;
13772  }
13773  else
13774  {
13775  LocationName = LocFront;
13776  }
13777  if(LocationName == "")
13778  {
13779  Utilities->CallLogPop(1036);
13780  return(false);
13781  }
13782  if((AVEntry0.SignallerControl) && (TDEntry.StartSpeed == 0))
13783  {
13784  Utilities->CallLogPop(1773);
13785  return(true);
13786  }
13787  else if((AVEntry0.SignallerControl) && (TDEntry.StartSpeed > 0))
13788  {
13789  LocationName = "";
13790  Utilities->CallLogPop(1784);
13791  return(false);
13792  }
13793  // here if not a signaller start entry so must be at least one more entry
13794  const TActionVectorEntry &AVEntry1 = TDEntry.ActionVector.at(1);
13795 
13796  // has to be at least 2 AV entries to pass the > 1 comma test in the preliminary check
13797  if(((AVEntry1.Command == "Frh") || (AVEntry1.Command == "Fjo") || (AVEntry1.Command == "F-nshs") || (AVEntry1.Command == "Fns")) && (AVEntry0.Command == "Snt")) // added Fjo at v2.0.0 for empty stock
13798  {
13799  //added F-nshs at v2.5.1 so can stay at a
13800  Utilities->CallLogPop(1037); //location until become a new shuttle service
13801  return(true); //added Fns at same time as saw no reason to exclude
13802  }
13803  AnsiString TimeLocLocationName;
13804  bool FoundFlag = false;
13805 
13806  for(unsigned int y = 0; y < TDEntry.ActionVector.size(); y++)
13807  {
13808  const TActionVectorEntry &AVEntry = TDEntry.ActionVector.at(y);
13809  if(AVEntry.FormatType == TimeLoc)
13810  {
13811  FoundFlag = true;
13812  TimeLocLocationName = AVEntry.LocationName;
13813  break;
13814  }
13815  }
13816  if(!FoundFlag)
13817  {
13818  Utilities->CallLogPop(853);
13819  return(false);
13820  }
13821  if(TimeLocLocationName == LocationName)
13822  {
13823  Utilities->CallLogPop(854);
13824  return(true);
13825  }
13826  Utilities->CallLogPop(855);
13827  return(false);
13828 }
13829 
13830 // ---------------------------------------------------------------------------
13831 
13832 bool TTrainController::CheckStartPositionValidity(int Caller, AnsiString RearElementStr, AnsiString FrontElementStr, bool GiveMessages)
13833 {
13834  // checks that the new train start elements are valid - both exist & are connected, and that not
13835  // attempting to start on a diverging leg (i.e. one segment on points & other on element connected to diverging leg)
13836  // & not starting with front on a continuation
13837  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CheckStartPositionValidity," + RearElementStr + "," + FrontElementStr);
13838  int RearPosition = 0, FrontPosition = 0, RearExitPos = 0;
13839 
13840  RearPosition = Track->GetTrackVectorPositionFromString(5, RearElementStr, GiveMessages);
13841  if(RearPosition < 0)
13842  // error message given in GetTrackVectorPositionFromString
13843  {
13844  Utilities->CallLogPop(759);
13845  return(false);
13846  }
13847  FrontPosition = Track->GetTrackVectorPositionFromString(6, FrontElementStr, GiveMessages);
13848  if(FrontPosition < 0)
13849  // error message given in GetTrackVectorPositionFromString
13850  {
13851  Utilities->CallLogPop(760);
13852  return(false);
13853  }
13854  TTrackElement RearTrackElement = Track->TrackElementAt(490, RearPosition);
13855  TTrackElement FrontTrackElement = Track->TrackElementAt(491, FrontPosition);
13856  TTrackType RearType = RearTrackElement.TrackType, FrontType = FrontTrackElement.TrackType;
13857 
13858  // check front & rear connected
13859  for(int x = 0; x < 4; x++)
13860  {
13861  if(RearTrackElement.Conn[x] == FrontPosition)
13862  {
13863  RearExitPos = x;
13864  break;
13865  }
13866  if(x == 3)
13867  {
13868  TimetableMessage(GiveMessages, "Front element: " + FrontTrackElement.ElementID + " not linked to rear element: " + RearTrackElement.ElementID);
13869  Utilities->CallLogPop(762);
13870  return(false);
13871  }
13872  }
13873  // check not starting with front on a continuation
13874  if(FrontType == Continuation)
13875  {
13876  TimetableMessage(GiveMessages, "Front of train attempting to start on a continuation at: " + FrontElementStr);
13877  Utilities->CallLogPop(937);
13878  return(false);
13879  }
13880  // check not starting on a level crossing
13881  if(Track->IsLCAtHV(43, FrontTrackElement.HLoc, FrontTrackElement.VLoc))
13882  {
13883  TimetableMessage(GiveMessages, "Train attempting to start on a level crossing at: " + FrontElementStr);
13884  Utilities->CallLogPop(1951);
13885  return(false);
13886  }
13887  if(Track->IsLCAtHV(44, RearTrackElement.HLoc, RearTrackElement.VLoc))
13888  {
13889  TimetableMessage(GiveMessages, "Train attempting to start on a level crossing at: " + RearElementStr);
13890  Utilities->CallLogPop(1952);
13891  return(false);
13892  }
13893  // check if trying to start on diverging leg of points
13894  if((RearType == Points) && (RearExitPos == 3))
13895  {
13896  TimetableMessage(GiveMessages, "Front of train attempting to start on element connected to diverging points at: " + RearElementStr);
13897  Utilities->CallLogPop(936);
13898  return(false);
13899  }
13900  if((FrontType == Points) && (RearTrackElement.ConnLinkPos[RearExitPos] == 3))
13901  {
13902  TimetableMessage(GiveMessages, "Rear of train attempting to start on element connected to diverging points at: " + FrontElementStr);
13903  Utilities->CallLogPop(1808);
13904  return(false);
13905  }
13906  Utilities->CallLogPop(905);
13907  return(true);
13908 }
13909 
13910 // ---------------------------------------------------------------------------
13911 
13912 bool TTrainController::CheckStartAllowable(int Caller, int RearPosition, int RearExitPos, AnsiString HeadCode, bool ReportFlag, TActionEventType &EventType)
13913 // Rear & front element validity already checked in CheckStartPositionValidity
13914 // This checks for points in correct orientation, no train at start position and not starting on a locked route
13915 {
13916  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CheckStartAllowable," + AnsiString(RearPosition) + "," +
13917  AnsiString(RearExitPos));
13918  TTrackElement RearTrackElement = Track->TrackElementAt(517, RearPosition);
13919 
13920  if(RearTrackElement.TrackType == Continuation)
13921  {
13922  EventType = FailTrainEntry;
13923  }
13924  else
13925  {
13926  EventType = FailCreateTrain;
13927  }
13928  int FrontPosition = RearTrackElement.Conn[RearExitPos];
13929  TTrackElement FrontTrackElement = Track->TrackElementAt(798, FrontPosition);
13930  int FrontEntryPos = RearTrackElement.ConnLinkPos[RearExitPos];
13931  TTrackType RearType = RearTrackElement.TrackType;
13932  TTrackType FrontType = FrontTrackElement.TrackType;
13933  AnsiString RearName, FrontName;
13934 
13935  if(RearTrackElement.ActiveTrackElementName != "")
13936  {
13937  RearName = RearTrackElement.ActiveTrackElementName;
13938  }
13939  else
13940  {
13941  RearName = RearTrackElement.ElementID;
13942  }
13943  if(FrontTrackElement.ActiveTrackElementName != "")
13944  {
13945  FrontName = FrontTrackElement.ActiveTrackElementName;
13946  }
13947  else
13948  {
13949  FrontName = FrontTrackElement.ElementID;
13950  }
13951  TPrefDirElement PrefDirElement; // needed for next function but not used
13952  int LockedVectorNumber; // needed for next function but not used
13953 
13954  if(AllRoutes->IsElementInLockedRouteGetPrefDirElementGetLockedVectorNumber(12, FrontPosition, FrontEntryPos, PrefDirElement, LockedVectorNumber))
13955  {
13956  if(ReportFlag)
13957  {
13958  if(EventType == FailCreateTrain)
13959  {
13960  EventType = FailCreateLockedRoute;
13961  }
13962  else
13963  {
13964  EventType = FailEnterLockedRoute;
13965  }
13966  LogActionError(47, HeadCode, "", EventType, FrontName);
13967  }
13968  Utilities->CallLogPop(940);
13969  return(false);
13970  }
13971  if(AllRoutes->IsElementInLockedRouteGetPrefDirElementGetLockedVectorNumber(13, RearPosition, RearExitPos, PrefDirElement, LockedVectorNumber))
13972  {
13973  if(ReportFlag)
13974  {
13975  if(EventType == FailCreateTrain)
13976  {
13977  EventType = FailCreateLockedRoute;
13978  }
13979  else
13980  {
13981  EventType = FailEnterLockedRoute;
13982  }
13983  LogActionError(48, HeadCode, "", EventType, RearName);
13984  }
13985  Utilities->CallLogPop(1809);
13986  return(false);
13987  }
13988  if((RearType != Bridge) && (RearTrackElement.TrainIDOnElement > -1))
13989  {
13990  if(ReportFlag)
13991  {
13992  LogActionError(27, HeadCode, "", EventType, RearName);
13993  }
13994  Utilities->CallLogPop(1810);
13995  return(false);
13996  }
13997  if((FrontType != Bridge) && (FrontTrackElement.TrainIDOnElement > -1))
13998  {
13999  if(ReportFlag)
14000  {
14001  if(EventType == FailCreateTrain)
14002  {
14003  LogActionError(28, HeadCode, "", EventType, FrontName);
14004  }
14005  else
14006  {
14007  LogActionError(43, HeadCode, "", EventType, RearName);
14008  }
14009  }
14010  Utilities->CallLogPop(941);
14011  return(false);
14012  }
14013  if(RearType == Bridge)
14014  {
14015  if((RearExitPos > 1) && (RearTrackElement.TrainIDOnBridgeTrackPos23 > -1))
14016  {
14017  if(ReportFlag)
14018  {
14019  LogActionError(29, HeadCode, "", EventType, RearName);
14020  }
14021  Utilities->CallLogPop(942);
14022  return(false);
14023  }
14024  if((RearExitPos < 2) && (RearTrackElement.TrainIDOnBridgeTrackPos01 > -1))
14025  {
14026  if(ReportFlag)
14027  {
14028  LogActionError(30, HeadCode, "", EventType, RearName);
14029  }
14030  Utilities->CallLogPop(943);
14031  return(false);
14032  }
14033  }
14034  if(FrontType == Bridge)
14035  {
14036  if((FrontEntryPos > 1) && (FrontTrackElement.TrainIDOnBridgeTrackPos23 > -1))
14037  {
14038  if(ReportFlag)
14039  {
14040  if(EventType == FailCreateTrain)
14041  {
14042  LogActionError(31, HeadCode, "", EventType, FrontName);
14043  }
14044  else
14045  {
14046  LogActionError(44, HeadCode, "", EventType, RearName);
14047  }
14048  }
14049  Utilities->CallLogPop(944);
14050  return(false);
14051  }
14052  if((FrontEntryPos < 2) && (FrontTrackElement.TrainIDOnBridgeTrackPos01 > -1))
14053  {
14054  if(ReportFlag)
14055  {
14056  if(EventType == FailCreateTrain)
14057  {
14058  LogActionError(45, HeadCode, "", EventType, FrontName);
14059  }
14060  else
14061  {
14062  LogActionError(46, HeadCode, "", EventType, RearName);
14063  }
14064  }
14065  Utilities->CallLogPop(945);
14066  return(false);
14067  }
14068  }
14069  EventType = FailCreatePoints;
14070  if(RearType == Points)
14071  {
14072  if(RearTrackElement.Attribute == 1)
14073  {
14074  if(ReportFlag)
14075  {
14076  LogActionError(33, HeadCode, "", FailCreatePoints, RearName);
14077  }
14078  Utilities->CallLogPop(933);
14079  return(false);
14080  }
14081  }
14082  if(FrontType == Points)
14083  {
14084  if(FrontTrackElement.Attribute == 1)
14085  {
14086  if(ReportFlag)
14087  {
14088  LogActionError(34, HeadCode, "", FailCreatePoints, FrontName);
14089  }
14090  Utilities->CallLogPop(934);
14091  return(false);
14092  }
14093  }
14094 
14095  //this section added at v2.9.1 to prevent entry for a train when there's a route set against it
14096  int HLoc = Track->TrackElementAt(1027, RearPosition).HLoc;
14097  int VLoc = Track->TrackElementAt(1028, RearPosition).VLoc;
14098  int ELink = Track->TrackElementAt(1029, RearPosition).Link[RearExitPos]; //if route entry corresponds to RearExitPos then it's set against the train
14099  int RouteNumber; //not used
14100  if(Track->TrackElementAt(1030, RearPosition).TrackType == Continuation)
14101  {
14102  if(AllRoutes->FindRouteNumberFromRoute2MultiMapNoErrors(8, HLoc, VLoc, ELink, RouteNumber))
14103  {
14104  EventType = FailEntryRouteSetAgainst;
14105  if(ReportFlag)
14106  {
14107  LogActionError(63, HeadCode, "", EventType, RearName);
14108  }
14109  Utilities->CallLogPop(2317);
14110  return(false);
14111  }
14112  }
14113  Utilities->CallLogPop(939);
14114  return(true);
14115 }
14116 
14117 // ---------------------------------------------------------------------------
14118 
14119 AnsiString TTrainController::GetRepeatHeadCode(int Caller, AnsiString BaseHeadCode, int RepeatNumber, int IncDigits)
14120 {
14121  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",GetRepeatHeadCode," + BaseHeadCode + "," + AnsiString(RepeatNumber) +
14122  "," + AnsiString(IncDigits));
14123  if(!Last2CharactersBothDigits(1, BaseHeadCode) && (IncDigits > 0))
14124  {
14125  throw Exception("Error, last 2 characters not both digits and IncDigits > 0 in GetRepeatHeadCode");
14126  }
14127  if(!Last2CharactersBothDigits(2, BaseHeadCode))
14128  {
14129  Utilities->CallLogPop(1893);
14130  return(BaseHeadCode);
14131  }
14132  int BaseDigits = BaseHeadCode.SubString(3, 2).ToInt();
14133  int NextRepeatDigits = BaseDigits + (IncDigits * RepeatNumber);
14134 
14135  while(NextRepeatDigits >= 100)
14136  {
14137  NextRepeatDigits -= 100; // rolls over after 99
14138  }
14139  AnsiString NextRepeatDigitsStr = AnsiString(NextRepeatDigits);
14140 
14141  if(NextRepeatDigitsStr.Length() < 2)
14142  {
14143  NextRepeatDigitsStr = AnsiString('0') + NextRepeatDigitsStr;
14144  }
14145  AnsiString NextRepeatHeadCode = BaseHeadCode.SubString(1, 2) + NextRepeatDigitsStr;
14146 
14147  Utilities->CallLogPop(1365);
14148  return(NextRepeatHeadCode);
14149 }
14150 
14151 // ---------------------------------------------------------------------------
14152 
14153 TDateTime TTrainController::GetRepeatTime(int Caller, TDateTime BasicTime, int RepeatNumber, int IncMinutes)
14154 {
14155  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",GetRepeatTime," + AnsiString(double(BasicTime)) + "," +
14156  AnsiString(RepeatNumber) + "," + AnsiString(IncMinutes));
14157  TDateTime NextRepeatTime = BasicTime + TDateTime(((double)(RepeatNumber * IncMinutes)) / 1440.0); // 1440 = no. of minutes in 24h
14158 
14159  Utilities->CallLogPop(1366);
14160  return(NextRepeatTime);
14161 }
14162 
14163 // ---------------------------------------------------------------------------
14164 
14165 bool TTrainController::CheckShuttleRepeatTime(int Caller, TDateTime ForwardEventTime, TDateTime ReverseEventTime, int RepeatMinutes)
14166 // For success the ForwardEventTime + repeat time should == ReverseEventTime (allow 10secs either way since converting to doubles)
14167 {
14168  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CheckShuttleRepeatTime," + AnsiString(double(ForwardEventTime)) + "," +
14169  AnsiString(double(ReverseEventTime)) + "," + AnsiString(RepeatMinutes));
14170  int ForwardSecs = int(double(ForwardEventTime) * 86400);
14171  int ReverseSecs = int(double(ReverseEventTime) * 86400);
14172  int RepeatSecs = RepeatMinutes * 60;
14173 
14174  if((ForwardSecs > (ReverseSecs - RepeatSecs + 10)) || (ForwardSecs < (ReverseSecs - RepeatSecs - 10)))
14175  {
14176  Utilities->CallLogPop(1367);
14177  return(false);
14178  }
14179  else
14180  {
14181  Utilities->CallLogPop(1368);
14182  return(true);
14183  }
14184 }
14185 
14186 // ---------------------------------------------------------------------------
14187 
14188 bool TTrainController::CheckNonRepeatingShuttleLinksAndSetData(int Caller, AnsiString MainHeadCode, AnsiString NonRepeatingHeadCode, bool GiveMessages)
14189 // check for proper non-repeating link cross references and that they have no repeats & that times are consistent
14190 
14191 /* Double crosslink (shuttle) table:
14192 
14193 Command Format OtherHead NonRepeating- LinkTrain- NonRepeating- Decsription
14194  Code ShuttleLink- EntryPtr ShuttleLink-
14195  HeadCode EntryPtr
14196 
14197 Snt-sh SNTShuttle Y (rtn shuttle) N Y (rtn sh) N Simple shuttle - no feeder service
14198 Frh-sh TimeCmdHeadCode Y (outwd shuttle) N Y (outwd sh) N Simple shuttle - no finishing service
14199 F-nshs FNSNonRepeatToShuttle N (shld be Y for outwd shuttle) Y (shld be N) Y (correct) N (correct) Feeder service link to shuttle
14200 Sns-sh SNSShuttle Y (rtn shuttle) Y (feeder) Y (rtn) Y (fdr) Shuttle link from feeder service
14201 Sns-fsh SNSNonRepeatFromShuttle N (shld be Y for rtn shuttle) Y (shld be N) Y (correct) N (correct) Finishing service link from shuttle
14202 Fns-sh FSHNewService Y (outwd shuttle) Y (finishing) Y (outwd sh) Y (finish) Shuttle link to finishing service
14203 
14204 Note: Any shuttle start can have any finish - feeder and finish, neither, feeder but no finish & vice versa.
14205 */
14206 
14207 {
14208  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CheckNonRepeatingShuttleLinksAndSetData," + MainHeadCode + "," +
14209  NonRepeatingHeadCode);
14210  int ForwardCount = 0;
14211  int ReverseCount = 0;
14212  unsigned int ForwardTDVectorNumber, ReverseTDVectorNumber;
14213  TActionVectorEntry *ReverseEntryPtr = 0, *ForwardEntryPtr = 0;
14214  // Forward corresponds to Main, Reverse to Other
14215  TTrainDataEntry *MainTrainDataPtr = 0;
14216  TTrainDataEntry *OtherTrainDataPtr = 0;
14217 
14218  // forward check
14219  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
14220  {
14221  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
14222  if(TDEntry.HeadCode == MainHeadCode)
14223  {
14224  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
14225  {
14226  TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
14227  if(AVEntry.NonRepeatingShuttleLinkHeadCode == NonRepeatingHeadCode)
14228  {
14229  MainTrainDataPtr = &TrainDataVector.at(x);
14230  ForwardEntryPtr = &AVEntry;
14231  ForwardCount++;
14232  ForwardTDVectorNumber = x;
14233  }
14234  }
14235  }
14236  }
14237  if(ForwardCount == 0)
14238  // this is an exception because the headcodes are selected in the same order as the forward check
14239  {
14240  throw Exception("Error, ForwardCount == 0 in CheckNonRepeatingShuttleLinksAndSetData after called with found values");
14241  }
14242  if(ForwardCount > 1)
14243  {
14244  SecondPassMessage(GiveMessages, "Error in timetable - found more than one reference to " + NonRepeatingHeadCode + " from a train whose headcode is " +
14245  MainHeadCode);
14246  TrainDataVector.clear();
14247  Utilities->CallLogPop(1061);
14248  return(false);
14249  }
14250  // reverse check
14251  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
14252  {
14253  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
14254  if(TDEntry.HeadCode == NonRepeatingHeadCode)
14255  {
14256  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
14257  {
14258  TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
14259  if(AVEntry.NonRepeatingShuttleLinkHeadCode == MainHeadCode)
14260  {
14261  OtherTrainDataPtr = &TrainDataVector.at(x);
14262  ReverseCount++;
14263  ReverseEntryPtr = &AVEntry;
14264  ReverseTDVectorNumber = x;
14265  }
14266  }
14267  }
14268  }
14269 
14270  if(ReverseCount == 0)
14271  {
14272  SecondPassMessage(GiveMessages, "Error in timetable - cross reference missing in either " + MainHeadCode + " or " + NonRepeatingHeadCode);
14273  TrainDataVector.clear();
14274  Utilities->CallLogPop(1062);
14275  return(false);
14276  }
14277  if(ReverseCount > 1)
14278  {
14279  SecondPassMessage(GiveMessages, "Error in timetable - found more than one reference to " + MainHeadCode + " from a train whose headcode is " +
14280  NonRepeatingHeadCode);
14281  TrainDataVector.clear();
14282  Utilities->CallLogPop(1063);
14283  return(false);
14284  }
14285  if(((ForwardEntryPtr->Command == "F-nshs") || (ForwardEntryPtr->Command == "Sns-fsh")) && (MainTrainDataPtr->ActionVector.back().FormatType == Repeat))
14286  {
14287  SecondPassMessage(GiveMessages, "Error in timetable - shuttle connecting train " + MainHeadCode + " shouldn't have a repeat");
14288  TrainDataVector.clear();
14289  Utilities->CallLogPop(1064);
14290  return(false);
14291  }
14292  if((ForwardEntryPtr->Command != "F-nshs") && (ForwardEntryPtr->Command != "Sns-fsh") && (MainTrainDataPtr->ActionVector.back().FormatType != Repeat))
14293  {
14294  SecondPassMessage(GiveMessages, "Error in timetable - shuttle train " + MainHeadCode + " does not have a repeat item");
14295  TrainDataVector.clear();
14296  Utilities->CallLogPop(1065);
14297  return(false);
14298  }
14299  if(ForwardEntryPtr->LocationName == "")
14300  {
14301  SecondPassMessage(GiveMessages, "Error in timetable - location error in cross referenced trains " + MainHeadCode + " and " + NonRepeatingHeadCode +
14302  ". One or other service does not have a location set");
14303  TrainDataVector.clear();
14304  Utilities->CallLogPop(1066);
14305  return(false);
14306  }
14307  if(ReverseEntryPtr->LocationName == "")
14308  {
14309  SecondPassMessage(GiveMessages, "Error in timetable - location error in cross referenced trains " + MainHeadCode + " and " + NonRepeatingHeadCode +
14310  ". One or other service does not have a location set");
14311  TrainDataVector.clear();
14312  Utilities->CallLogPop(1067);
14313  return(false);
14314  }
14315  if(ForwardEntryPtr->LocationName != ReverseEntryPtr->LocationName)
14316  {
14317  SecondPassMessage(GiveMessages, "Error in timetable - cross referenced train " + NonRepeatingHeadCode +
14318  " is at a different location to the referencing train " + MainHeadCode);
14319  TrainDataVector.clear();
14320  Utilities->CallLogPop(1068);
14321  return(false);
14322  }
14323  if(ForwardEntryPtr->Command == "F-nshs")
14324  // i.e. the non repeating link into the shuttle service
14325  {
14326  if(ForwardEntryPtr->EventTime != ReverseEntryPtr->EventTime)
14327  {
14328  SecondPassMessage(GiveMessages, "Error in timetable - shuttle in-link service " + MainHeadCode +
14329  " finish time not consistent with start time of shuttle service " + NonRepeatingHeadCode);
14330  TrainDataVector.clear();
14331  Utilities->CallLogPop(1069);
14332  return(false);
14333  }
14334  }
14335  if(ForwardEntryPtr->Command == "Fns-sh")
14336  // i.e. the non repeating link out from the shuttle service
14337  {
14338  if(!CheckNonRepeatingShuttleLinkTime(0, ForwardEntryPtr->EventTime, ReverseEntryPtr->EventTime,
14339  MainTrainDataPtr->ActionVector.back().RearStartOrRepeatMins, MainTrainDataPtr->ActionVector.back().NumberOfRepeats))
14340  {
14341  SecondPassMessage(GiveMessages, "Error in timetable - service " + NonRepeatingHeadCode + ", which links out from shuttle service " + MainHeadCode +
14342  ", has the wrong start time. It should correspond to the finish time of the last shuttle.");
14343  TrainDataVector.clear();
14344  Utilities->CallLogPop(1070);
14345  return(false);
14346  }
14347  }
14348  if((ForwardEntryPtr->Command == "F-nshs") || (ForwardEntryPtr->Command == "Sns-fsh"))
14349  // i.e. a non repeating link to or from the shuttle service
14350  {
14351  if(ReverseTDVectorNumber == ForwardTDVectorNumber)
14352  {
14353  SecondPassMessage(GiveMessages, "Error in timetable - the non repeating link service " + NonRepeatingHeadCode +
14354  " appears in the same sequence as the corresponding shuttle service " + MainHeadCode);
14355  TrainDataVector.clear();
14356  Utilities->CallLogPop(1071);
14357  return(false);
14358  }
14359  }
14360 /* it's allowed to have a different description
14361  if((ForwardEntryPtr->Command == "F-nshs") || (ForwardEntryPtr->Command == "Sns-fsh"))//i.e. a non repeating link to or from the shuttle service
14362  {
14363  if((MainTrainDataPtr->Description != "") && (OtherTrainDataPtr->Description != "") && (MainTrainDataPtr->Description != OtherTrainDataPtr->Description))
14364  {
14365  SecondPassMessage(GiveMessages, "Error in timetable - the non repeating link service " + NonRepeatingHeadCode + " has a different description to the corresponding shuttle service " + MainHeadCode);
14366  TrainDataVector.clear();
14367  Utilities->CallLogPop(1072);
14368  return false;
14369  }
14370  }
14371 */
14372  if(ForwardEntryPtr->Command == "Sns-sh")
14373  {
14374  if(ReverseEntryPtr->Command != "F-nshs")
14375  {
14376  SecondPassMessage(GiveMessages, "Error in timetable - unable to find a corresponding 'F-nshs' event for the 'Sns-sh' train whose headcode is " +
14377  MainHeadCode + " and is a new shuttle service formed from the service with headcode " + NonRepeatingHeadCode);
14378  TrainDataVector.clear();
14379  Utilities->CallLogPop(1073);
14380  return(false);
14381  }
14382  }
14383  if(ForwardEntryPtr->Command == "F-nshs")
14384  {
14385  if(ReverseEntryPtr->Command != "Sns-sh")
14386  {
14387  SecondPassMessage(GiveMessages, "Error in timetable - unable to find a corresponding 'Sns-sh' event for the 'F-nshs' train whose headcode is " +
14388  MainHeadCode + " and forms a new shuttle service with headcode " + NonRepeatingHeadCode);
14389  TrainDataVector.clear();
14390  Utilities->CallLogPop(1074);
14391  return(false);
14392  }
14393  else
14394  {
14395  ForwardEntryPtr->LinkedTrainEntryPtr = OtherTrainDataPtr;
14396  ReverseEntryPtr->NonRepeatingShuttleLinkEntryPtr = MainTrainDataPtr;
14397  if(OtherTrainDataPtr->Description == "")
14398  {
14399  OtherTrainDataPtr->Description = MainTrainDataPtr->Description;
14400  }
14401  // Probably redundant as this is continued from the earlier service when the changeover happens (also may not be set here yet if a service continuation)
14402  OtherTrainDataPtr->MaxRunningSpeed = MainTrainDataPtr->MaxRunningSpeed;
14403  // Probably redundant as this is continued from the earlier service when the changeover happens (also may not be set here yet if a service continuation)
14404  }
14405  }
14406  if(ForwardEntryPtr->Command == "Sns-fsh")
14407  {
14408  if(ReverseEntryPtr->Command != "Fns-sh")
14409  {
14410  SecondPassMessage(GiveMessages,
14411  "Error in timetable - unable to find a corresponding 'Fns-sh' event for the 'Sns-fsh' non-shuttle service whose headcode is " + MainHeadCode +
14412  " formed from a shuttle service with headcode " + NonRepeatingHeadCode);
14413  TrainDataVector.clear();
14414  Utilities->CallLogPop(1075);
14415  return(false);
14416  }
14417  }
14418  if(ForwardEntryPtr->Command == "Fns-sh")
14419  {
14420  if(ReverseEntryPtr->Command != "Sns-fsh")
14421  {
14422  SecondPassMessage(GiveMessages,
14423  "Error in timetable - unable to find a corresponding 'Sns-fsh' event for the 'Fns-sh' shuttle service whose headcode is " + MainHeadCode +
14424  " and forms a new non-shuttle service with headcode " + NonRepeatingHeadCode);
14425  TrainDataVector.clear();
14426  Utilities->CallLogPop(1076);
14427  return(false);
14428  }
14429  else
14430  {
14431  ForwardEntryPtr->NonRepeatingShuttleLinkEntryPtr = OtherTrainDataPtr;
14432  // links to the non-repeating non-shuttle linked service
14433  ReverseEntryPtr->LinkedTrainEntryPtr = MainTrainDataPtr;
14434  // needed for creating formatted timetable
14435  if(OtherTrainDataPtr->Description == "")
14436  {
14437  OtherTrainDataPtr->Description = MainTrainDataPtr->Description;
14438  }
14439  // Probably redundant as this is continued from the earlier service when the changeover happens (also may not be set here yet if a service continuation)
14440  OtherTrainDataPtr->MaxRunningSpeed = MainTrainDataPtr->MaxRunningSpeed;
14441  // Probably redundant as this is continued from the earlier service when the changeover happens (also may not be set here yet if a service continuation)
14442  }
14443  }
14444  Utilities->CallLogPop(1077);
14445  return(true);
14446 }
14447 
14448 // ---------------------------------------------------------------------------
14449 
14450 bool TTrainController::CheckNonRepeatingShuttleLinkTime(int Caller, TDateTime ForwardEventTime, TDateTime ReverseEventTime, int RepeatMinutes, int RepeatNumber)
14451 // Forward train is the finish shuttle entry 'Fns-sh'.
14452 // The Reverse (new non-repeating service) time must == Forward time + (RepeatMins * RepeatNumber) but allow 10 secs either side
14453 {
14454  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CheckNonRepeatingShuttleLinkTime," + AnsiString(double(ForwardEventTime))
14455  + "," + AnsiString(double(ReverseEventTime)) + "," + AnsiString(RepeatMinutes) + "," + AnsiString(RepeatNumber));
14456  int ForwardSecs = int(double(ForwardEventTime) * 86400);
14457  int ReverseSecs = int(double(ReverseEventTime) * 86400);
14458  int RepeatSecs = RepeatMinutes * RepeatNumber * 60;
14459 
14460  if((ReverseSecs > (ForwardSecs + RepeatSecs + 10)) || (ReverseSecs < (ForwardSecs + RepeatSecs - 10)))
14461  {
14462  Utilities->CallLogPop(1369);
14463  return(false);
14464  }
14465  else
14466  {
14467  Utilities->CallLogPop(1370);
14468  return(true);
14469  }
14470 }
14471 
14472 // ---------------------------------------------------------------------------
14473 
14474 bool TTrainController::CheckShuttleServiceIntegrity(int Caller, TTrainDataEntry *TDEntryPtr, bool GiveMessages)
14475 // check that each shuttle start ends either in Fns or Fxx-sh (though a single service can't end in Fxx-sh), and that
14476 // when the Fxx-sh is reached it references the original start and not another shuttle - not allowed to link two shuttles,
14477 // don't ever need to and as designed would skip repeats.
14478 
14479 // enter with TDEntry a shuttle start - Snt-sh or Sns-sh
14480 {
14481  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CheckShuttleServiceIntegrity," + AnsiString(TDEntryPtr->HeadCode));
14482  if(TDEntryPtr->ActionVector.back().FormatType != Repeat)
14483  {
14484  throw Exception("Error - last entry in " + TDEntryPtr->HeadCode + " service is not a repeat - should have already found this error");
14485  }
14486  TTrainDataEntry *ShuttleStartAddress = TDEntryPtr;
14487  AnsiString OriginalHeadCode = TDEntryPtr->HeadCode;
14488  AnsiString LastActionCommand = (TDEntryPtr->ActionVector.end() - 2)->Command;
14489 
14490  if((LastActionCommand != "Fns") && (LastActionCommand != "Fns-sh") && (LastActionCommand != "Frh-sh"))
14491  {
14492  SecondPassMessage(GiveMessages, "Error in timetable - last event in shuttle service " + TDEntryPtr->HeadCode + " is not 'Fns', 'Fns-sh' or 'Frh-sh'");
14493  TrainDataVector.clear();
14494  Utilities->CallLogPop(1091);
14495  return(false);
14496  }
14497  while(LastActionCommand == "Fns")
14498  {
14499  TDEntryPtr = (TDEntryPtr->ActionVector.end() - 2)->LinkedTrainEntryPtr;
14500  LastActionCommand = (TDEntryPtr->ActionVector.end() - 2)->Command;
14501  if((LastActionCommand != "Fns") && (LastActionCommand != "Fns-sh") && (LastActionCommand != "Frh-sh"))
14502  {
14503  SecondPassMessage(GiveMessages,
14504  "Error in timetable - last event in a continuation shuttle service (i.e links back to a shuttle) whose headcode is " + TDEntryPtr->HeadCode +
14505  " is not 'Fns', 'Fns-sh' or 'Frh-sh'");
14506  TrainDataVector.clear();
14507  Utilities->CallLogPop(1092);
14508  return(false);
14509  }
14510  }
14511  // exit the 'while' with LastActionCommand FSH-XX
14512  if((TDEntryPtr->ActionVector.end() - 2)->LinkedTrainEntryPtr != ShuttleStartAddress)
14513  {
14514  SecondPassMessage(GiveMessages, "Error in timetable - the event that ends service " + TDEntryPtr->HeadCode +
14515  " is a shuttle finish, but it doesn't link back to the start of the original shuttle starting service " + OriginalHeadCode +
14516  ". The linking of two or more shuttles is not permitted.");
14517  TrainDataVector.clear();
14518  Utilities->CallLogPop(1093);
14519  return(false);
14520  }
14521  Utilities->CallLogPop(1094);
14522  return(true);
14523 }
14524 
14525 // ---------------------------------------------------------------------------
14526 
14527 void TTrainController::TimetableMessage(bool GiveMessages, AnsiString Message)
14528 {
14529  if(!GiveMessages)
14530  {
14531  return;
14532  }
14533  // if(ServiceReference == "") ShowMessage(Message);
14534  if(!CheckHeadCodeValidity(12, false, ServiceReference))
14535  {
14536  ShowMessage(Message);
14537  }
14538  // changed from above at v2.3.0 as a meaningless value for 'Timetable invalid - unable to find a valid start time on its own line' (uses last entry text)
14539  // false means don't give messages within the function
14540  else
14541  {
14542  ShowMessage("Service " + ServiceReference + ": " + Message);
14543  }
14544 }
14545 
14546 // ---------------------------------------------------------------------------
14547 
14548 void TTrainController::SecondPassMessage(bool GiveMessages, AnsiString Message)
14549 {
14550  if(!GiveMessages)
14551  {
14552  return;
14553  }
14554  ShowMessage(Message);
14555 }
14556 
14557 // $$$$$$$$$$$$$$$$$$$$$$$ End of Timetable Functions $$$$$$$$$$$$$$$$$$$$$$$
14558 // ---------------------------------------------------------------------------
14559 
14560 void TTrainController::LogActionError(int Caller, AnsiString HeadCode, AnsiString OtherHeadCode, TActionEventType ActionEventType, AnsiString LocationID)
14561 // FailTrainEntry: 06:00:10 HELD: 2F43 can't enter railway, train obstructing entry position 57-N5
14562 // FailCreateTrain: 06:00:10 HELD: 2F43 can't be created, train obstructing start position 57-N5
14563 // FailCreateLockedRoute: 06:00:10 HELD: 2F43 can't be created on a locked route - start position 57-N5
14564 // FailEnterLockedRoute: 06:00:10 HELD: 2F43 can't enter on a locked route - start position 57-N5
14565 // FailCreatePoints: 06:00:10 HELD: 2F43 can't be created, points set to diverge at start position 57-N5
14566 // FailUnexpectedExitRailway: 06:00:10 ERROR: 2F43 left railway unexpectedly at position 57-N5
14567 // FailIncorrectExit: 06:00:10 ERROR: 2F43 left railway at an incorrect exit at position 57-N5
14568 // FailSPAD: 06:00:10 ERROR: 2F43 PASSED SIGNAL AT DANGER at position 57-N5
14569 // FailLockedRoute: 06:00:10 ERROR: SPAD Risk! Signals reset ahead of train, at position 57-N5
14570 // FailLocTooShort: 06:00:10 ERROR: 2F43 failed to split - location too short at Essex Road
14571 // FailSplitDueToOtherTrain: 06:00:10 HELD: 2F43 unable to split - another train is obstructing at Essex Road
14572 // FailCrashed: 06:00:10: ERROR: 2F43 CRASHED INTO 3F43 at position 46-N7
14573 // FailDerailed: 06:00:10: ERROR: 2F43 DERAILED at position 46-N7
14574 // FailUnexpectedBuffers: 06:00:10: ERROR: 2F43 stopped at buffers unexpectedly at position 46-N7
14575 // FailMissedArrival: 06:00:10: ERROR: 2F43 failed to stop at Essex Road;
14576 // FailMissedSplit: 06:00:10: ERROR: 2F43 failed to split at Essex Road
14577 // FailMissedJBO: 06:00:10: ERROR: 2F43 failed to be joined by join other train at Essex Road
14578 // FailMissedJoinOther: 06:00:10: ERROR: 2F43 failed to join other train at Essex Road
14579 // FailMissedTerminate: 06:00:10: ERROR: 2F43 failed to terminate at Essex Road
14580 // FailMissedNewService: 06:00:10: ERROR: 2F43 failed to form new service at Essex Road
14581 // FailMissedExitRailway: 06:00:10: ERROR: 2F43 failed to exit railway
14582 // FailMissedChangeDirection: 06:00:10: ERROR: 2F43 failed to change direction at Essex Road
14583 // FailMissedPass: 06:00:10: ERROR: 2F43 failed to pass Essex Road
14584 // FailBuffersPreventingStart: 06:00:10: ERROR: 2F43 facing buffers and unable to start at Essex Road
14585 // FailBufferCrash: 06:00:10: ERROR: 2F43 CRASHED INTO BUFFERS at 46-N7
14586 // FailLevelCrossingCrash: 06:00:10: ERROR: 2F43 CRASHED INTO ROAD TRAFFIC AT A LEVEL CROSSING at 46-N7
14587 // RouteForceCancelled: 06:00:10: ERROR: 2F43 forced a route cancellation by occupying it incorrectly at 46-N7
14588 // WaitingForJBO: 06:00:10: WARNING: 2F43 waiting to join 3F43 at Essex Road
14589 // WaitingForFJO: 06:00:10: WARNING: 2F43 waiting to be joined by 3F43 at Essex Road
14590 // FailEntryRouteSetAgainst: 06:00:10: WARNING: 2F43 can't enter railway, route set against it at entry position 57-N5
14591 {
14592  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",LogActionError," + HeadCode + "," + OtherHeadCode + "," +
14593  AnsiString(ActionEventType) + "," + LocationID);
14594  AnsiString BaseLog = "", Prefix = "", ErrorLog = "", WarningStr = "";
14595 
14596  TDateTime ActualTime = TrainController->TTClockTime; //moved from lower down at v2.9.1
14597  AnsiString TimeAndHeadCode = Utilities->Format96HHMMSS(ActualTime) + " " + HeadCode; //added at v2.9.1 to give more info to user
14598 
14599  Prefix = " ERROR: ";
14600  if(ActionEventType == FailTrainEntry)
14601  {
14602  Prefix = " HELD: ";
14603  ErrorLog = " can't enter railway, train obstructing entry position ";
14604  WarningStr = " can't enter railway, train obstructing entry position ";
14605  Display->WarningLog(1, TimeAndHeadCode + WarningStr + LocationID);
14606  }
14607  else if(ActionEventType == FailEntryRouteSetAgainst) //added at v2.9.1
14608  {
14609  Prefix = " HELD: ";
14610  ErrorLog = " can't enter railway, route set against it at entry position ";
14611  WarningStr = " can't enter railway, route set against it at entry position ";
14612  Display->WarningLog(10, TimeAndHeadCode + WarningStr + LocationID);
14613  }
14614  else if(ActionEventType == FailCreateTrain)
14615  {
14616  Prefix = " HELD: ";
14617  ErrorLog = " can't be created, train obstructing ";
14618  WarningStr = " can't be created, train obstructing ";
14619  Display->WarningLog(2, TimeAndHeadCode + WarningStr + LocationID);
14620  }
14621  else if(ActionEventType == FailCreateLockedRoute)
14622  {
14623  Prefix = " HELD: ";
14624  ErrorLog = " can't be created on a locked route at ";
14625  WarningStr = " can't be created on a locked route at ";
14626  Display->WarningLog(4, TimeAndHeadCode + WarningStr + LocationID);
14627  }
14628  else if(ActionEventType == FailEnterLockedRoute)
14629  {
14630  Prefix = " HELD: ";
14631  ErrorLog = " can't enter on a locked route at ";
14632  WarningStr = " can't enter on a locked route at ";
14633  Display->WarningLog(5, TimeAndHeadCode + WarningStr + LocationID);
14634  }
14635  else if(ActionEventType == FailCreatePoints)
14636  {
14637  Prefix = " HELD: ";
14638  ErrorLog = " can't be created, diverging points at ";
14639  WarningStr = " can't be created, diverging points at ";
14640  Display->WarningLog(3, TimeAndHeadCode + WarningStr + LocationID);
14641  }
14642  else if(ActionEventType == FailUnexpectedExitRailway)
14643  {
14644  ErrorLog = " left railway unexpectedly at ";
14645  UnexpectedExits++;
14646  }
14647  else if(ActionEventType == FailIncorrectExit)
14648  {
14649  ErrorLog = " left railway at an incorrect exit at ";
14650  IncorrectExits++;
14651  }
14652  else if(ActionEventType == FailLocTooShort)
14653  {
14654  ErrorLog = " failed to split - location too short at ";
14655  WarningStr = " failed to split, location too short at ";
14656  Display->WarningLog(6, TimeAndHeadCode + WarningStr + LocationID);
14657  }
14658  else if(ActionEventType == FailSplitDueToOtherTrain)
14659  {
14660  Prefix = " HELD: ";
14661  ErrorLog = " unable to split - other train obstructing at ";
14662  WarningStr = " unable to split - other train obstructing at ";
14663  Display->WarningLog(7, TimeAndHeadCode + WarningStr + LocationID);
14664  }
14665  else if(ActionEventType == FailUnexpectedBuffers)
14666  {
14667  ErrorLog = " stopped at buffers unexpectedly at position ";
14668  }
14669  else if(ActionEventType == FailMissedArrival)
14670  {
14671  ErrorLog = " failed to stop at ";
14672  MissedStops++;
14673  }
14674  else if(ActionEventType == FailMissedSplit)
14675  {
14676  ErrorLog = " failed to split at ";
14678  }
14679  else if(ActionEventType == FailMissedJBO)
14680  {
14681  ErrorLog = " failed to be joined by other train at ";
14683  }
14684  else if(ActionEventType == FailMissedJoinOther)
14685  {
14686  ErrorLog = " failed to join other train at ";
14688  }
14689  else if(ActionEventType == FailMissedTerminate)
14690  {
14691  ErrorLog = " failed to terminate at ";
14693  }
14694  else if(ActionEventType == FailMissedNewService)
14695  {
14696  ErrorLog = " failed to form new service at ";
14698  }
14699  else if(ActionEventType == FailMissedExitRailway)
14700  {
14701  ErrorLog = " failed to exit railway ";
14703  }
14704  else if(ActionEventType == FailMissedChangeDirection)
14705  {
14706  ErrorLog = " failed to change direction at ";
14708  }
14709  else if(ActionEventType == FailMissedPass)
14710  {
14711  ErrorLog = " failed to pass ";
14713  }
14714  else if(ActionEventType == FailBuffersPreventingStart)
14715  {
14716  ErrorLog = " facing buffers and unable to start at ";
14717  }
14718  else if(ActionEventType == FailDerailed)
14719  {
14720  ErrorLog = " DERAILED at position ";
14721  Prefix = " DERAILMENT: ";
14722  Derailments++;
14723  }
14724  else if(ActionEventType == FailBufferCrash)
14725  {
14726  ErrorLog = " CRASHED INTO BUFFERS at ";
14727  Prefix = " CRASH: ";
14728  CrashedTrains++;
14729  }
14730  else if(ActionEventType == FailLevelCrossingCrash)
14731  {
14732  ErrorLog = " CRASHED INTO ROAD TRAFFIC AT A LEVEL CROSSING at ";
14733  Prefix = " CRASH: ";
14734  CrashedTrains++;
14735  }
14736  else if(ActionEventType == FailCrashed)
14737  {
14738  ErrorLog = " CRASHED INTO " + OtherHeadCode + " at position ";
14739  Prefix = " CRASH: ";
14740  CrashedTrains++;
14741  CrashedTrains++;
14742  }
14743  else if(ActionEventType == FailSPAD)
14744  {
14745  ErrorLog = " PASSED SIGNAL AT DANGER at position ";
14746  Prefix = " SPAD: ";
14747  SPADEvents++;
14748  }
14749  else if(ActionEventType == FailLockedRoute)
14750  {
14751  ErrorLog = "Signals reset ahead of train, route cancelled at position ";
14752  Prefix = " SPAD RISK: ";
14753  SPADRisks++;
14754  }
14755  else if(ActionEventType == RouteForceCancelled)
14756  {
14757  ErrorLog = " forced a route cancellation by occupying it incorrectly at ";
14758  }
14759  else if(ActionEventType == WaitingForJBO)
14760  {
14761  Prefix = " WARNING: ";
14762  ErrorLog = " waiting to join " + OtherHeadCode + " at ";
14763  WarningStr = " waiting to join " + OtherHeadCode + " at ";
14764  Display->WarningLog(8, TimeAndHeadCode + WarningStr + LocationID);
14765  }
14766  else if(ActionEventType == WaitingForFJO)
14767  {
14768  Prefix = " WARNING: ";
14769  ErrorLog = " waiting to be joined by " + OtherHeadCode + " at ";
14770  WarningStr = " waiting to be joined by " + OtherHeadCode + " at ";
14771  Display->WarningLog(9, TimeAndHeadCode + WarningStr + LocationID);
14772  }
14773 
14774  BaseLog = Utilities->Format96HHMMSS(ActualTime) + Prefix + HeadCode;
14775  Display->PerformanceLog(4, BaseLog + ErrorLog + LocationID);
14776  Utilities->CallLogPop(1371);
14777 }
14778 
14779 // ---------------------------------------------------------------------------
14780 
14782 {
14783 /*
14784  TrainDataEntry
14785  AnsiString HeadCode, Description;//null on creation
14786  int StartSpeed, MaxRunningSpeed;//both kph
14787  int RepeatNumber;
14788  TActionVector ActionVector;
14789  TTrainOperatingDataVector TrainOperatingDataVector;//no of repeats + 1
14790  TTrainDataEntry() {StartSpeed=0; MaxRunningSpeed=0; RepeatNumber=0;}
14791 
14792  ActionVectorEntry
14793  TTimetableEntryType FormatType;
14794  TDateTime EventTime, ArrivalTime, DepartureTime;//zeroed on creation so change to -1 as a marker for 'not set'
14795  AnsiString LocationName, Command, OtherHeadCode;//null on creation
14796  TActionVectorEntry *OtherHeadCodeStartingEntryPtr;
14797  int RearStartOrRepeatMins, FrontStartOrRepeatDigits;
14798  int RepeatNumber;
14799 
14800  TrainOperatingData
14801  int Mass, MaxBrakeRate, PowerAtRail;//kg;m/s/s;W
14802  int TrainID;
14803  TRunningEntry RunningEntry;
14804  TDateTime StartTime;
14805  AnsiString HeadCode;
14806 */
14807  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SaveTrainDataVectorToFile");
14808  std::ofstream OutFile("TrainData.csv");
14809 
14810  if(OutFile == 0)
14811  {
14812  ShowMessage("Output file TrainData.csv failed to open");
14813  Utilities->CallLogPop(1372);
14814  return;
14815  }
14816  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
14817  {
14818  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
14819  OutFile << "HeadCode" << ',' << "Description" << ',' << "StartSpeed" << ',' << "MaxRunningSpeed" << ',' << "NumberOfTrains" << '\n' << '\n';
14820 
14821  OutFile << TDEntry.HeadCode.c_str() << ',' << TDEntry.Description.c_str()
14822  << ',' << TDEntry.StartSpeed << ',' << TDEntry.MaxRunningSpeed << ',' << TDEntry.NumberOfTrains << '\n' << '\n';
14823 
14824  OutFile << ',' << "FormatType" << ',' << "EventTime" << ',' << "ArrivalTime" << ',' << "DepartureTime" << ',' << "LocationName" << ',' << "Command" <<
14825  ',' << "OtherHeadCode" << ',' << "LinkedTrainEntryPtr" << ',' << "RearStartOrRepeatMins" << ',' << "FrontStartOrRepeatDigits" << ',' <<
14826  "RepeatNumber" << '\n' << '\n';
14827  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
14828  {
14829  TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
14830  AnsiString TimetableEntryTypeStr;
14831  // NoFormat, TimeLoc, TimeTimeLoc, TimeCmd, StartNew, TimeCmdHeadCode, FinRemHere, FNSNonRepeatToShuttle, SNTShuttle, SNSShuttle, SNSNonRepeatFromShuttle, FSHNewService, Repeat
14832  switch(AVEntry.FormatType)
14833  {
14834  case 0:
14835  {
14836  TimetableEntryTypeStr = "NoFormat";
14837  break;
14838  }
14839 
14840  case 1:
14841  {
14842  TimetableEntryTypeStr = "TimeLoc";
14843  break;
14844  }
14845 
14846  case 2:
14847  {
14848  TimetableEntryTypeStr = "TimeTimeLoc";
14849  break;
14850  }
14851 
14852  case 3:
14853  {
14854  TimetableEntryTypeStr = "TimeCmd";
14855  break;
14856  }
14857 
14858  case 4:
14859  {
14860  TimetableEntryTypeStr = "StartNew";
14861  break;
14862  }
14863 
14864  case 5:
14865  {
14866  TimetableEntryTypeStr = "TimeCmdHeadCode";
14867  break;
14868  }
14869 
14870  case 6:
14871  {
14872  TimetableEntryTypeStr = "FinRemHere";
14873  break;
14874  }
14875 
14876  case 7:
14877  {
14878  TimetableEntryTypeStr = "FNSShuttle";
14879  break;
14880  }
14881 
14882  case 8:
14883  {
14884  TimetableEntryTypeStr = "SNTShuttle";
14885  break;
14886  }
14887 
14888  case 9:
14889  {
14890  TimetableEntryTypeStr = "SNSShuttle";
14891  break;
14892  }
14893 
14894  case 10:
14895  {
14896  TimetableEntryTypeStr = "SNSNonRepeatFromShuttle";
14897  break;
14898  }
14899 
14900  case 11:
14901  {
14902  TimetableEntryTypeStr = "FSHNewService";
14903  break;
14904  }
14905 
14906  case 12:
14907  {
14908  TimetableEntryTypeStr = "Repeat";
14909  break;
14910  }
14911 
14912  default:
14913  {
14914  TimetableEntryTypeStr = "Default";
14915  break;
14916  }
14917  }
14918  OutFile << ',' << TimetableEntryTypeStr.c_str() << ',' << Utilities->Format96HHMM(AVEntry.EventTime).c_str() << ',' << Utilities->Format96HHMM
14919  (AVEntry.ArrivalTime).c_str() << ',' << Utilities->Format96HHMM(AVEntry.DepartureTime).c_str() << ',' << AVEntry.LocationName.c_str()
14920  << ',' << AVEntry.Command.c_str() << ',' << AVEntry.OtherHeadCode.c_str()
14921  << ',' << AVEntry.LinkedTrainEntryPtr << ',' << AVEntry.RearStartOrRepeatMins << ',' << AVEntry.FrontStartOrRepeatDigits << ',' <<
14922  AVEntry.NumberOfRepeats << '\n';
14923  }
14924  OutFile << '\n';
14925  OutFile << ',' << ',' << "Mass" << ',' << "MaxBrakeRate" << ',' << "PowerAtRail" << ',' << "TrainID" << ',' << "RunningEntry" << '\n' << '\n';
14926  for(unsigned int y = 0; y < TrainDataVector.at(x).TrainOperatingDataVector.size(); y++)
14927  {
14928  TTrainOperatingData TOD = TrainDataVector.at(x).TrainOperatingDataVector.at(y);
14929  AnsiString RunningEntryStr;
14930  // NotStarted, Running, Exited
14931  switch(TOD.RunningEntry)
14932  {
14933  case 0:
14934  {
14935  RunningEntryStr = "NotStarted";
14936  break;
14937  }
14938 
14939  case 1:
14940  {
14941  RunningEntryStr = "Running";
14942  break;
14943  }
14944 
14945  case 2:
14946  {
14947  RunningEntryStr = "Exited";
14948  break;
14949  }
14950  }
14951  OutFile << ',' << ',' << TOD.TrainID << ',' << RunningEntryStr.c_str() << ',' << '\n';
14952  }
14953  OutFile << '\n';
14954  }
14955  OutFile.close();
14956  Utilities->CallLogPop(1373);
14957 }
14958 
14959 // ---------------------------------------------------------------------------
14960 
14961 void TTrainController::StopTTClockMessage(int Caller, AnsiString Message)
14962 // ShowMessage stops everything so this function used where a message is needed when may be in Operating mode.
14963 // The timetable Restart and BaseTimes are reset so the timetable clock stops & restarts when 'OK' button pressed
14964 {
14965  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",StopTTClockMessage," + Message);
14966  StopTTClockFlag = true; // so TTClock stopped during MasterClockTimer function
14968  ShowMessage(Message);
14969  BaseTime = TDateTime::CurrentDateTime();
14970  StopTTClockFlag = false;
14971  Utilities->CallLogPop(1374);
14972 }
14973 
14974 // ---------------------------------------------------------------------------
14975 
14976 void TTrainController::SaveSessionTrains(int Caller, std::ofstream &SessionFile)
14977 // save *TrainDataEntryPtr & *ActionVectorEntryPtr as integer offsets
14978 // from the start of the relevant vectors. Can't save the pointer values
14979 // as these will be different each time the vectors are created
14980 {
14981  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SaveSessionTrains");
14982  Utilities->SaveFileInt(SessionFile, TrainVector.size());
14983  for(unsigned int x = 0; x < TrainVector.size(); x++)
14984  {
14985  TrainVectorAt(55, x).SaveOneSessionTrain(0, SessionFile);
14986  }
14987  Utilities->CallLogPop(1375);
14988 }
14989 
14990 // ---------------------------------------------------------------------------
14991 
14992 void TTrainController::LoadSessionTrains(int Caller, std::ifstream &SessionFile)
14993 {
14994  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",LoadSessionTrains");
14995  int NumberOfTrains = Utilities->LoadFileInt(SessionFile);
14996  TTrain *NewTrain = new TTrain(1, 0, 0, "", 0, 1, 0, 0, 0, (TTrainMode)0, 0, 0, 0, 0, 0); // have to have >0 for mass, else have divide
14997  // by zero error in calculating AValue, use 1
14998  for(int x = 0; x < NumberOfTrains; x++)
14999  {
15000  *NewTrain = TTrain(2, 0, 0, "", 0, 1, 0, 0, 0, (TTrainMode)0, 0, 0, 0, 0, 0); // have to have >0 for mass, else have divide
15001  // by zero error in calculating AValue, use 1
15002  NewTrain->LoadOneSessionTrain(0, SessionFile);
15003  if((NewTrain->EntrySpeed < 1) && (NewTrain->PowerAtRail < 1))
15004  // added at v2.4.0. have to include as that value not stored in session file
15005  {
15006  NewTrain->StoppedWithoutPower = true;
15007  }
15008  TrainVector.push_back(*NewTrain);
15009  LastTrainLoaded = x;
15010  }
15011  delete NewTrain;
15012  Utilities->CallLogPop(1376);
15013 }
15014 
15015 // ---------------------------------------------------------------------------
15016 
15017 bool TTrainController::CheckSessionTrains(int Caller, std::ifstream &InFile)
15018 {
15019  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CheckSessionTrains");
15020  int NumberOfTrains;
15021 
15022  if(!Utilities->CheckAndReadFileInt(InFile, 0, 10000, NumberOfTrains))
15023  {
15024  Utilities->CallLogPop(1377);
15025  return(false);
15026  }
15027  for(int x = 0; x < NumberOfTrains; x++)
15028  {
15029  if(!(TTrain::CheckOneSessionTrain(InFile)))
15030  {
15031  Utilities->CallLogPop(1378);
15032  return(false);
15033  }
15034  }
15035  Utilities->CallLogPop(1379);
15036  return(true);
15037 }
15038 
15039 // ---------------------------------------------------------------------------
15040 
15041 void TTrainController::SaveSessionLockedRoutes(int Caller, std::ofstream &SessionFile)
15042 {
15043  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SaveSessionLockedRoutes");
15044  Utilities->SaveFileInt(SessionFile, AllRoutes->LockedRouteVector.size());
15045  for(unsigned int x = 0; x < AllRoutes->LockedRouteVector.size(); x++)
15046  {
15047  Utilities->SaveFileInt(SessionFile, AllRoutes->LockedRouteVector.at(x).RouteNumber);
15048  Utilities->SaveFileInt(SessionFile, AllRoutes->LockedRouteVector.at(x).TruncateTrackVectorPosition);
15049  Utilities->SaveFileInt(SessionFile, AllRoutes->LockedRouteVector.at(x).LastTrackVectorPosition);
15050  Utilities->SaveFileInt(SessionFile, AllRoutes->LockedRouteVector.at(x).LastXLinkPos);
15051  Utilities->SaveFileDouble(SessionFile, double(AllRoutes->LockedRouteVector.at(x).LockStartTime));
15052  }
15053  Utilities->CallLogPop(1380);
15054 }
15055 
15056 // ---------------------------------------------------------------------------
15057 
15058 void TTrainController::LoadSessionLockedRoutes(int Caller, std::ifstream &SessionFile)
15059 {
15060  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",LoadSessionLockedRoutes");
15061  TAllRoutes::TLockedRouteClass LockedRouteObject;
15062  int LockedRouteVectorSize = Utilities->LoadFileInt(SessionFile);
15063 
15064  for(int x = 0; x < LockedRouteVectorSize; x++)
15065  {
15066  LockedRouteObject.RouteNumber = Utilities->LoadFileInt(SessionFile);
15067  LockedRouteObject.TruncateTrackVectorPosition = Utilities->LoadFileInt(SessionFile);
15068  LockedRouteObject.LastTrackVectorPosition = Utilities->LoadFileInt(SessionFile);
15069  LockedRouteObject.LastXLinkPos = Utilities->LoadFileInt(SessionFile);
15070  double LockStartTimeDouble = Utilities->LoadFileDouble(SessionFile);
15071  LockedRouteObject.LockStartTime = TDateTime(LockStartTimeDouble);
15072  AllRoutes->LockedRouteVector.push_back(LockedRouteObject);
15073  }
15074  Utilities->CallLogPop(1381);
15075 }
15076 
15077 // ---------------------------------------------------------------------------
15078 
15079 bool TTrainController::CheckSessionLockedRoutes(int Caller, std::ifstream &SessionFile)
15080 {
15081  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CheckSessionLockedRoutes");
15082  int LockedRouteVectorSize;
15083 
15084  if(!Utilities->CheckAndReadFileInt(SessionFile, 0, 10000, LockedRouteVectorSize))
15085  {
15086  Utilities->CallLogPop(1382);
15087  return(false);
15088  }
15089  for(int x = 0; x < LockedRouteVectorSize; x++)
15090  {
15091  if(!Utilities->CheckFileInt(SessionFile, 0, 1000000))
15092  {
15093  Utilities->CallLogPop(1383);
15094  return(false);
15095  }
15096  if(!Utilities->CheckFileInt(SessionFile, 0, 1000000))
15097  {
15098  Utilities->CallLogPop(1384);
15099  return(false);
15100  }
15101  if(!Utilities->CheckFileInt(SessionFile, 0, 1000000))
15102  {
15103  Utilities->CallLogPop(1385);
15104  return(false);
15105  }
15106  if(!Utilities->CheckFileInt(SessionFile, 0, 3))
15107  {
15108  Utilities->CallLogPop(1386);
15109  return(false);
15110  }
15111  if(!Utilities->CheckFileDouble(SessionFile))
15112  {
15113  Utilities->CallLogPop(1387);
15114  return(false);
15115  }
15116  }
15117  Utilities->CallLogPop(1388);
15118  return(true);
15119 }
15120 
15121 // ---------------------------------------------------------------------------
15122 
15123 void TTrainController::SaveSessionContinuationAutoSigEntries(int Caller, std::ofstream &SessionFile)
15124 {
15125  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SaveSessionContinuationAutoSigEntries");
15126  Utilities->SaveFileInt(SessionFile, ContinuationAutoSigVector.size());
15127  for(unsigned int x = 0; x < ContinuationAutoSigVector.size(); x++)
15128  {
15129  Utilities->SaveFileInt(SessionFile, ContinuationAutoSigVector.at(x).RouteNumber);
15130  Utilities->SaveFileInt(SessionFile, ContinuationAutoSigVector.at(x).AccessNumber);
15131  Utilities->SaveFileDouble(SessionFile, ContinuationAutoSigVector.at(x).FirstDelay);
15132  Utilities->SaveFileDouble(SessionFile, ContinuationAutoSigVector.at(x).SecondDelay);
15133  Utilities->SaveFileDouble(SessionFile, ContinuationAutoSigVector.at(x).ThirdDelay);
15134  Utilities->SaveFileDouble(SessionFile, double(ContinuationAutoSigVector.at(x).PassoutTime));
15135  }
15136  Utilities->CallLogPop(1389);
15137 }
15138 
15139 // ---------------------------------------------------------------------------
15140 
15141 void TTrainController::LoadSessionContinuationAutoSigEntries(int Caller, std::ifstream &SessionFile)
15142 {
15143  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",LoadSessionContinuationAutoSigEntries");
15144  TContinuationAutoSigEntry ContinuationAutoSigObject;
15145  int ContinuationAutoSigVectorSize = Utilities->LoadFileInt(SessionFile);
15146 
15147  for(int x = 0; x < ContinuationAutoSigVectorSize; x++)
15148  {
15149  ContinuationAutoSigObject.RouteNumber = Utilities->LoadFileInt(SessionFile);
15150  ContinuationAutoSigObject.AccessNumber = Utilities->LoadFileInt(SessionFile);
15151  ContinuationAutoSigObject.FirstDelay = Utilities->LoadFileDouble(SessionFile);
15152  ContinuationAutoSigObject.SecondDelay = Utilities->LoadFileDouble(SessionFile);
15153  ContinuationAutoSigObject.ThirdDelay = Utilities->LoadFileDouble(SessionFile);
15154  double PassoutTimeDouble = Utilities->LoadFileDouble(SessionFile);
15155  ContinuationAutoSigObject.PassoutTime = TDateTime(PassoutTimeDouble);
15156  ContinuationAutoSigVector.push_back(ContinuationAutoSigObject);
15157  }
15158  Utilities->CallLogPop(1390);
15159 }
15160 
15161 // ---------------------------------------------------------------------------
15162 
15163 bool TTrainController::CheckSessionContinuationAutoSigEntries(int Caller, std::ifstream &SessionFile)
15164 {
15165  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CheckSessionContinuationAutoSigEntries");
15166  int ContinuationAutoSigVectorSize;
15167 
15168  if(!Utilities->CheckAndReadFileInt(SessionFile, 0, 10000, ContinuationAutoSigVectorSize))
15169  {
15170  Utilities->CallLogPop(1391);
15171  return(false);
15172  }
15173  for(int x = 0; x < ContinuationAutoSigVectorSize; x++)
15174  {
15175  if(!Utilities->CheckFileInt(SessionFile, 0, 1000000))
15176  {
15177  Utilities->CallLogPop(1392);
15178  return(false);
15179  }
15180  if(!Utilities->CheckFileInt(SessionFile, 0, 3))
15181  {
15182  Utilities->CallLogPop(1393);
15183  return(false);
15184  }
15185  if(!Utilities->CheckFileDouble(SessionFile))
15186  {
15187  Utilities->CallLogPop(1405);
15188  return(false);
15189  }
15190  if(!Utilities->CheckFileDouble(SessionFile))
15191  {
15192  Utilities->CallLogPop(1406);
15193  return(false);
15194  }
15195  if(!Utilities->CheckFileDouble(SessionFile))
15196  {
15197  Utilities->CallLogPop(1407);
15198  return(false);
15199  }
15200  if(!Utilities->CheckFileDouble(SessionFile))
15201  {
15202  Utilities->CallLogPop(1394);
15203  return(false);
15204  }
15205  }
15206  Utilities->CallLogPop(1395);
15207  return(true);
15208 }
15209 
15210 // ---------------------------------------------------------------------------
15211 
15212 /*
15213  class TContinuationTrainExpectationEntry //for expected trains at continuation entries
15214  {
15215  public:
15216  AnsiString Description; ///< service description
15217  AnsiString HeadCode; ///< service headcode
15218  int RepeatNumber; ///< service RepeatNumber
15219  int IncrementalMinutes; ///< Repeat separation in minutes
15220  int IncrementalDigits; ///< Repeat headcode separation
15221  int VectorPosition; ///< TrackVectorPosition for the continuation element
15222  TTrainDataEntry *TrainDataEntryPtr; ///< points to the service entry in the timetable's TrainDataVector
15223  };
15224 
15225 
15226  typedef std::multimap<TDateTime,TContinuationTrainExpectationEntry> TContinuationTrainExpectationMultiMap;
15227  typedef pair<TDateTime, TContinuationTrainExpectationEntry> TContinuationTrainExpectationMultiMapPair;
15228 */
15229 
15231 // build this into timetable load so session loading can use it too
15232 // being a multimap it automatically sorts in ascending EventTime order
15233 {
15234  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",BuildContinuationTrainExpectationMultiMap");
15236  // need to clear as this called twice when load a session
15237  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
15238  {
15239  TTrainDataEntry & TDEntry = TrainDataVector.at(x);
15240  const TActionVectorEntry &AVFirstEntry = TDEntry.ActionVector.at(0);
15241  const TActionVectorEntry &AVLastEntry = TDEntry.ActionVector.at(TDEntry.ActionVector.size() - 1);
15242 
15243  if(AVFirstEntry.Command == "Snt")
15244  // new train (no need to include Snt-sh since they can't start at a continuation)
15245  {
15248  {
15250  CTEEntry.VectorPosition = AVFirstEntry.RearStartOrRepeatMins;
15251  // retains this value for all repeats
15252  CTEEntry.RepeatNumber = 0; // for first entry
15253  CTEEntry.TrainDataEntryPtr = &TDEntry;
15254  // retains this value for all repeats
15255  CTEEntry.HeadCode = TDEntry.HeadCode;
15256  CTEEntry.Description = TDEntry.Description;
15257  CTEEntry.IncrementalMinutes = 0;
15258  CTEEntry.IncrementalDigits = 0;
15259  if(AVLastEntry.FormatType == Repeat)
15260  {
15261  CTEEntry.IncrementalMinutes = AVLastEntry.RearStartOrRepeatMins;
15262  // retains this value or 0 for all repeats
15263  CTEEntry.IncrementalDigits = AVLastEntry.FrontStartOrRepeatDigits;
15264  // retains this value or 0 for all repeats
15265  }
15266  CTEMMP.first = AVFirstEntry.EventTime;
15267  CTEMMP.second = CTEEntry;
15268  ContinuationTrainExpectationMultiMap.insert(CTEMMP);
15269  // base entry
15270  if(TDEntry.NumberOfTrains > 1)
15271  {
15272  if(AVLastEntry.FormatType != Repeat)
15273  {
15274  throw Exception("Error, Last ActionVectorEntry not a repeat in BuildContinuationTrainExpectationMultiMap");
15275  }
15276  for(int y = 1; y < TDEntry.NumberOfTrains; y++)
15277  {
15278  CTEEntry.RepeatNumber = y;
15279  CTEEntry.HeadCode = GetRepeatHeadCode(23, TDEntry.HeadCode, y, AVLastEntry.FrontStartOrRepeatDigits);
15280  // CTEEntry.VectorPosition stays same
15281  CTEMMP.first = GetRepeatTime(3, AVFirstEntry.EventTime, y, AVLastEntry.RearStartOrRepeatMins);
15282  CTEMMP.second = CTEEntry;
15283  ContinuationTrainExpectationMultiMap.insert(CTEMMP);
15284  }
15285  }
15286  }
15287  }
15288  }
15289  Utilities->CallLogPop(1396);
15290 }
15291 
15292 // ---------------------------------------------------------------------------
15293 
15295 {
15296  // called when WarningFlashCount == 0 or when press zoomout button
15297  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",PlotTrainsInZoomOutMode");
15298  if(!Display->ZoomOutFlag)
15299  {
15300  Utilities->CallLogPop(1156);
15301  return;
15302  }
15303  for(unsigned int x = 0; x < TrainVector.size(); x++)
15304  {
15305  // plot blanks & track for all train, even if to be overplotted, since when flashing need to overplot all anyway
15306  // if OldPlotElement[x] == -1 then ignore (not plotted)
15308  TrainVectorAt(57, x).PlotTrainInZoomOutMode(0, Flash);
15309  }
15310  Display->Update();
15311  // need to keep this since Update() not called for PlotSmallOutput as too slow
15312  Utilities->CallLogPop(742);
15313 }
15314 
15315 // ---------------------------------------------------------------------------
15316 
15317 TTrain &TTrainController::TrainVectorAt(int Caller, int VecPos)
15318 {
15319  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",TrainVectorAt," + AnsiString(VecPos));
15320  if((VecPos < 0) || (VecPos >= (int)TrainVector.size()))
15321  {
15322  throw Exception("Out of Range Error, vector size: " + AnsiString(TrainVector.size()) + ", VecPos: " + AnsiString(VecPos) + " in TrainVectorAt");
15323  }
15324  Utilities->CallLogPop(740);
15325  return(TrainVector.at(VecPos));
15326 }
15327 
15328 // ---------------------------------------------------------------------------
15329 
15330 void TTrainController::CreateFormattedTimetable(int Caller, AnsiString RailwayTitle, AnsiString TimetableTitle, AnsiString CurDir)
15331 {
15332  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CreateFormattedTimetable");
15333  AnsiString RetStr = "", PartStr = "";
15334 
15335 
15336 /*
15337  Have description & mass etc for train at top - header, then array of actions
15338 
15339  class TActionVectorEntry
15340  {
15341  public:
15342  AnsiString LocationName, Command, OtherHeadCode, NonRepeatingShuttleLinkHeadCode;
15344  bool SignallerControl;
15346  bool Warning;
15348  int NumberOfRepeats;
15350  int RearStartOrRepeatMins, FrontStartOrRepeatDigits;
15352  TDateTime EventTime, ArrivalTime, DepartureTime;
15354  TNumList ExitList;
15356  TTimetableFormatType FormatType;
15358  TTimetableLocationType LocationType;
15360  TTimetableSequenceType SequenceType;
15362  TTimetableShuttleLinkType ShuttleLinkType;
15364  TTrainDataEntry *LinkedTrainEntryPtr;
15366  TTrainDataEntry *NonRepeatingShuttleLinkEntryPtr;
15368 
15369  typedef std::vector<TActionVectorEntry> TActionVector;//contains all actions for a single train
15370 
15371  enum TRunningEntry {NotStarted, Running, Exited};//contains status info for each train
15372 
15373  class TTrainOperatingData
15374  {
15375  public:
15376  int TrainID;
15377  TActionEventType EventReported;
15378  TRunningEntry RunningEntry;
15379 
15380  //inline function
15381  TTrainOperatingData() {TrainID = -1; EventReported= NoEvent; RunningEntry=NotStarted;}//ID -1 = marker for not running
15382  };
15383 
15384  typedef std::vector<TTrainOperatingData> TTrainOperatingDataVector;
15385 
15386  class TTrainDataEntry
15387  {
15388  public:
15389  AnsiString HeadCode, ServiceReference, Description;
15391  double MaxBrakeRate;
15393  double MaxRunningSpeed;
15395  double PowerAtRail;
15397  int Mass;
15399  int NumberOfTrains;
15401  int SignallerSpeed;
15403  int StartSpeed;
15405  TActionVector ActionVector;
15407  TTrainOperatingDataVector TrainOperatingDataVector;
15409 
15410  //inline function
15411  TTrainDataEntry() {StartSpeed=0; MaxRunningSpeed=0; NumberOfTrains=0;}
15412  };
15413 
15414  typedef std::vector<TTrainDataEntry> TTrainDataVector;//object is a member of TTrainController & contains the whole timetable
15415 
15416  //formatted timetable types
15417  class TOneTrainFormattedEntry
15418  {
15419  AnsiString Action;//includes location if relevanr
15420  AnsiString Time;
15421  };
15422 
15423  typedef std::vector<TOneTrainFormattedEntry> TOneFormattedTrainVector;
15424 
15425  class TOneCompleteFormattedTrain//headcode + list of actions
15426  {
15427  public:
15428  AnsiString HeadCode;
15429  TOneFormattedTrainVector OneFormattedTrainVector;
15430  };
15431 
15432  typedef std::vector<TOneCompleteFormattedTrain> TOneCompleteFormattedTrainVector;//list af all repeats
15433 
15434  class TTrainFormattedInformation//contains all information for a single TT entry (including repeats)
15435  {
15436  public:
15437  AnsiString Header;//description, mass, power, brake rate etc
15438  int NumberOfTrains;// number of repeats + 1
15439  TOneCompleteFormattedTrainVector OneCompleteFormattedTrainVector;//list af all repeats
15440  };
15441 
15442 
15443  typedef std::vector<TTrainFormattedInformation> TAllFormattedTrains;//all timetable in formatted form
15444  //end of formatted timetable types
15445 
15446 */
15447 
15448  AnsiString TTFileName = TDateTime::CurrentDateTime().FormatString("dd-mm-yyyy hh.nn.ss");
15449 
15450  // format "16/06/2009 20:55:17"
15451  // avoid characters in filename:= / \ : * ? " < > |
15452  TTFileName = CurDir + "\\Formatted timetables\\Timetable " + TTFileName + "; " + RailwayTitle + "; " + TimetableTitle + ".csv";
15453 
15454  AnsiString ShortTTName = "";
15455 
15456  for(int x = TTFileName.Length(); x > 0; x--)
15457  {
15458  if(TTFileName[x] == '\\')
15459  {
15460  ShortTTName = TTFileName.SubString(x + 1, TTFileName.Length() - x - 4);
15461  break;
15462  }
15463  }
15464 
15465  ShowMessage("Creates two timetables named " + ShortTTName +
15466  " in the 'Formatted timetables' folder, one in service order in '.csv' format, and one in chronological order in '.txt' format");
15467 
15468  Screen->Cursor = TCursor(-11); // Hourglass
15469 
15470  AnsiString FormatNoDPStr = "#######0";
15471  AnsiString TableTitle = "", TimetableTimeStr = "", MassStr = "", PowerStr = "", BrakeStr = "", MaxSpeedStr = "", FirstHeadCode = "", Header = "";
15472 
15474  TableTitle = "Railway: " + RailwayTitle + "; Timetable: " + TimetableTitle + "; Start time: " + TimetableTimeStr;
15475  TAllFormattedTrains *AllTTTrains = new TAllFormattedTrains;
15476 
15477  // all timetable in formatted form
15478  //create the AllTTTrains vector
15479  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
15480  {
15481  MassStr = "", PowerStr = "", BrakeStr = "", MaxSpeedStr = "";
15482  const TTrainDataEntry &TrainDataEntry = TrainDataVector.at(x);
15483  if(TrainDataEntry.Mass > 0)
15484  {
15485  MassStr = "; Mass " + AnsiString::FormatFloat(FormatNoDPStr, ((double)TrainDataEntry.Mass) / 1000) + "Te; ";
15486  }
15487  if(TrainDataEntry.PowerAtRail > 0)
15488  {
15489  PowerStr = "Power " + AnsiString::FormatFloat(FormatNoDPStr, TrainDataEntry.PowerAtRail / 1000 / 0.8) + "kW; ";
15490  }
15491  if(TrainDataEntry.MaxBrakeRate > 0)
15492  {
15493  BrakeStr = "Brake force " + AnsiString::FormatFloat(FormatNoDPStr, (TrainDataEntry.MaxBrakeRate * TrainDataEntry.Mass / 9810)) + "Te; ";
15494  }
15495  if(TrainDataEntry.MaxRunningSpeed > 0)
15496  {
15497  MaxSpeedStr = "Maximum speed " + AnsiString::FormatFloat(FormatNoDPStr, TrainDataEntry.MaxRunningSpeed) + " km/h";
15498  }
15499  FirstHeadCode = TrainDataEntry.HeadCode;
15500  int IncDigits = 0, IncMinutes = 0;
15501  const TActionVector &ActionVector = TrainDataEntry.ActionVector;
15502  if(!ActionVector.empty())
15503  {
15504  if(ActionVector.at(ActionVector.size() - 1).FormatType == Repeat)
15505  {
15506  IncDigits = ActionVector.at(ActionVector.size() - 1).FrontStartOrRepeatDigits;
15507  IncMinutes = ActionVector.at(ActionVector.size() - 1).RearStartOrRepeatMins;
15508  }
15509  }
15510  TTrainFormattedInformation OneTTLine;
15511  // contains all information for a single TT entry (including repeats)
15512  for(int y = 0; y < TrainDataEntry.NumberOfTrains; y++)
15513  {
15514  OneTTLine.Header = "";
15515  if((TrainDataEntry.Description != "") && (MassStr != ""))
15516  {
15517  OneTTLine.Header = TrainDataEntry.Description + MassStr + PowerStr + BrakeStr + MaxSpeedStr;
15518  }
15519  else if(TrainDataEntry.Description != "")
15520  {
15521  OneTTLine.Header = TrainDataEntry.Description;
15522  }
15523  OneTTLine.NumberOfTrains = TrainDataEntry.NumberOfTrains;
15524  TOneCompleteFormattedTrain OneTTTrain; // headcode + list of actions
15525  for(unsigned int z = 0; z < ActionVector.size(); z++)
15526  {
15527  TOneTrainFormattedEntry OneTTEntry;
15528  OneTTTrain.HeadCode = GetRepeatHeadCode(24, FirstHeadCode, y, IncDigits);
15529  TActionVectorEntry ActionVectorEntry = ActionVector.at(z);
15530  AnsiString PartStr = "", TimeStr = "";
15531 /*
15532  enum TTimetableFormatType {NoFormat, TimeLoc, TimeTimeLoc, TimeCmd, StartNew, TimeCmdHeadCode, FinRemHere,
15533  FNSNonRepeatToShuttle, SNTShuttle, SNSShuttle, SNSNonRepeatFromShuttle, FSHNewService, Repeat, PassTime,
15534  ExitRailway};
15535  enum TTimetableSequenceType {NoSequence, Start, Finish, Intermediate, SequTypeForRepeatEntry};
15536  enum TTimetableLocationType {NoLocation, AtLocation, EnRoute, LocTypeForRepeatEntry};
15537  enum TTimetableShuttleLinkType {NoShuttleLink, NotAShuttleLink, ShuttleLink, ShuttleLinkTypeForRepeatEntry};
15538 */
15539  if(ActionVectorEntry.SequenceType == Start)
15540  {
15541  if(ActionVectorEntry.FormatType == StartNew)
15542  {
15543  if(ActionVectorEntry.LocationName != "")
15544  {
15545  if(Track->TrackElementAt(742, ActionVectorEntry.RearStartOrRepeatMins).TrackType == Continuation)
15546  {
15547  PartStr = "Enters at " + ActionVectorEntry.LocationName;
15548  }
15549  else
15550  {
15551  PartStr = "Created at " + ActionVectorEntry.LocationName;
15552  }
15553  }
15554  else // may be a named continuation or other element, and if so report that
15555  {
15556  AnsiString LocName = Track->TrackElementAt(739, ActionVectorEntry.RearStartOrRepeatMins).ActiveTrackElementName;
15557  if(Track->TrackElementAt(740, ActionVectorEntry.RearStartOrRepeatMins).TrackType == Continuation)
15558  {
15559  if(LocName != "")
15560  {
15561  PartStr = "Enters at " + LocName;
15562  }
15563  else // use rear position if it's a continuation
15564  {
15565  PartStr = "Enters at " + Track->TrackElementAt(737, ActionVectorEntry.RearStartOrRepeatMins).ElementID;
15566  }
15567  }
15568  else // not a continuation
15569  {
15570  if(LocName != "")
15571  // if not a continuation then LocName should be same as ActionVectorEntry.LocationName
15572  // but include anyway
15573  {
15574  PartStr = "Created at " + LocName;
15575  }
15576  else // use rear position again
15577  {
15578  PartStr = "Created at " + Track->TrackElementAt(741, ActionVectorEntry.RearStartOrRepeatMins).ElementID;
15579  }
15580  }
15581  }
15582  TimeStr = Utilities->Format96HHMM(GetRepeatTime(20, ActionVectorEntry.EventTime, y, IncMinutes));
15583  }
15584  else if(ActionVectorEntry.FormatType == SNTShuttle)
15585  {
15586  if(y == 0) // first train
15587  {
15588  PartStr = "Enters at " + ActionVectorEntry.LocationName;
15589  TimeStr = Utilities->Format96HHMM(GetRepeatTime(21, ActionVectorEntry.EventTime, y, IncMinutes));
15590  }
15591  else
15592  {
15593  PartStr = "Repeat shuttle service at " + ActionVectorEntry.LocationName + " from ";
15594  TimeStr = GetRepeatHeadCode(45, ActionVectorEntry.OtherHeadCode, y - 1, IncDigits) + " at " +
15595  Utilities->Format96HHMM(GetRepeatTime(26, ActionVectorEntry.EventTime, y, IncMinutes));
15596  } // y-1 for headcode above since it is the last repeat value that the train is from
15597 
15598  }
15599  else if(ActionVectorEntry.Command == "Sfs")
15600  {
15601  PartStr = "New service at " + ActionVectorEntry.LocationName + " split from";
15602  TimeStr = GetRepeatHeadCode(33, ActionVectorEntry.OtherHeadCode, y, IncDigits) + " at " +
15603  Utilities->Format96HHMM(GetRepeatTime(24, ActionVectorEntry.EventTime, y, IncMinutes));
15604  }
15605  else if(ActionVectorEntry.Command == "Sns")
15606  {
15607  PartStr = "New service at " + ActionVectorEntry.LocationName + " from";
15608  TimeStr = GetRepeatHeadCode(34, ActionVectorEntry.OtherHeadCode, y, IncDigits) + " at " +
15609  Utilities->Format96HHMM(GetRepeatTime(25, ActionVectorEntry.EventTime, y, IncMinutes));
15610  }
15611  else if(ActionVectorEntry.FormatType == SNSShuttle)
15612  {
15613  if(y == 0) // first entry from shuttle
15614  {
15615  PartStr = "New service at " + ActionVectorEntry.LocationName + " from";
15616  TimeStr = ActionVectorEntry.NonRepeatingShuttleLinkHeadCode + " at " +
15617  Utilities->Format96HHMM(GetRepeatTime(27, ActionVectorEntry.EventTime, y, IncMinutes));
15618  }
15619  else
15620  {
15621  PartStr = "Repeat shuttle service at " + ActionVectorEntry.LocationName + " from ";
15622  TimeStr = GetRepeatHeadCode(35, ActionVectorEntry.OtherHeadCode, y - 1, IncDigits) + " at " +
15623  Utilities->Format96HHMM(GetRepeatTime(22, ActionVectorEntry.EventTime, y, IncMinutes));
15624  } // y-1 for headcode above since it is the last repeat value that the train is from
15625 
15626  }
15627  else if(ActionVectorEntry.FormatType == SNSNonRepeatFromShuttle)
15628  {
15629  PartStr = "New service at " + ActionVectorEntry.LocationName + " from";
15630  // need repeat for the non-repeating headcode as it's the last train of the repeating shuttle
15631  TTrainDataEntry *TDE = ActionVectorEntry.LinkedTrainEntryPtr;
15632  AnsiString FirstHeadCode = TDE->HeadCode;
15633  int LastRepeatNumber = TDE->NumberOfTrains - 1;
15634  // a shuttle has to have at least 1 repeat
15635  int IncrementalDigits = TDE->ActionVector.at(TDE->ActionVector.size() - 1).FrontStartOrRepeatDigits;
15636  TimeStr = GetRepeatHeadCode(36, FirstHeadCode, LastRepeatNumber, IncrementalDigits) + " at " +
15637  Utilities->Format96HHMM(GetRepeatTime(23, ActionVectorEntry.EventTime, y, IncMinutes));
15638  }
15639  }
15640  else if(ActionVectorEntry.SequenceType == Intermediate)
15641  {
15642  if(ActionVectorEntry.FormatType == TimeTimeLoc)
15643  {
15644  // here need 2 entries if times different so push the first right away & the second later
15645  // if times same just give the arrival entry
15646  if(ActionVectorEntry.DepartureTime != ActionVectorEntry.ArrivalTime)
15647  {
15648  PartStr = "Arrives at " + ActionVectorEntry.LocationName;
15649  TimeStr = Utilities->Format96HHMM(GetRepeatTime(4, ActionVectorEntry.ArrivalTime, y, IncMinutes));
15650  OneTTEntry.Action = PartStr;
15651  OneTTEntry.Time = TimeStr;
15652  OneTTTrain.OneFormattedTrainVector.push_back(OneTTEntry);
15653  PartStr = "Departs from " + ActionVectorEntry.LocationName;
15654  TimeStr = Utilities->Format96HHMM(GetRepeatTime(5, ActionVectorEntry.DepartureTime, y, IncMinutes));
15655  }
15656  else
15657  {
15658  PartStr = "Arrives & departs " + ActionVectorEntry.LocationName;
15659  TimeStr = Utilities->Format96HHMM(GetRepeatTime(29, ActionVectorEntry.ArrivalTime, y, IncMinutes));
15660  }
15661  }
15662  else if((ActionVectorEntry.FormatType == TimeLoc) && (ActionVectorEntry.ArrivalTime != TDateTime(-1)))
15663  {
15664  PartStr = "Arrives at " + ActionVectorEntry.LocationName;
15665  TimeStr = Utilities->Format96HHMM(GetRepeatTime(6, ActionVectorEntry.ArrivalTime, y, IncMinutes));
15666  }
15667  else if((ActionVectorEntry.FormatType == TimeLoc) && (ActionVectorEntry.ArrivalTime == TDateTime(-1)))
15668  {
15669  PartStr = "Departs from " + ActionVectorEntry.LocationName;
15670  TimeStr = Utilities->Format96HHMM(GetRepeatTime(7, ActionVectorEntry.DepartureTime, y, IncMinutes));
15671  }
15672  else if(ActionVectorEntry.FormatType == PassTime)
15673  {
15674  PartStr = "Passes " + ActionVectorEntry.LocationName;
15675  TimeStr = Utilities->Format96HHMM(GetRepeatTime(8, ActionVectorEntry.EventTime, y, IncMinutes));
15676  }
15677  else if(ActionVectorEntry.Command == "jbo")
15678  {
15679  PartStr = "Joined at " + ActionVectorEntry.LocationName + " by";
15680  TimeStr = GetRepeatHeadCode(37, ActionVectorEntry.OtherHeadCode, y, IncDigits) + " at " +
15681  Utilities->Format96HHMM(GetRepeatTime(9, ActionVectorEntry.EventTime, y, IncMinutes));
15682  }
15683  else if(ActionVectorEntry.Command == "fsp")
15684  {
15685  PartStr = "Splits from front at " + ActionVectorEntry.LocationName + " to form";
15686  TimeStr = GetRepeatHeadCode(38, ActionVectorEntry.OtherHeadCode, y, IncDigits) + " at " +
15687  Utilities->Format96HHMM(GetRepeatTime(10, ActionVectorEntry.EventTime, y, IncMinutes));
15688  }
15689  else if(ActionVectorEntry.Command == "rsp")
15690  {
15691  PartStr = "Splits from rear at " + ActionVectorEntry.LocationName + " to form";
15692  TimeStr = GetRepeatHeadCode(39, ActionVectorEntry.OtherHeadCode, y, IncDigits) + " at " +
15693  Utilities->Format96HHMM(GetRepeatTime(11, ActionVectorEntry.EventTime, y, IncMinutes));
15694  }
15695  else if(ActionVectorEntry.Command == "cdt")
15696  {
15697  PartStr = "Changes direction at " + ActionVectorEntry.LocationName;
15698  TimeStr = Utilities->Format96HHMM(GetRepeatTime(12, ActionVectorEntry.EventTime, y, IncMinutes));
15699  }
15700  }
15701  else if(ActionVectorEntry.SequenceType == Finish)
15702  {
15703  if(ActionVectorEntry.Command == "Fns")
15704  {
15705  PartStr = "At " + ActionVectorEntry.LocationName + " forms new service";
15706  TimeStr = GetRepeatHeadCode(40, ActionVectorEntry.OtherHeadCode, y, IncDigits) + " at " +
15707  Utilities->Format96HHMM(GetRepeatTime(13, ActionVectorEntry.EventTime, y, IncMinutes));
15708  }
15709  else if(ActionVectorEntry.Command == "F-nshs")
15710  {
15711  PartStr = "At " + ActionVectorEntry.LocationName + " forms new service";
15712  TimeStr = ActionVectorEntry.NonRepeatingShuttleLinkHeadCode + " at " + Utilities->Format96HHMM
15713  (GetRepeatTime(17, ActionVectorEntry.EventTime, y, IncMinutes));
15714  }
15715  else if((ActionVectorEntry.Command == "Fns-sh") && (y < (TrainDataEntry.NumberOfTrains - 1)))
15716  {
15717  PartStr = "At " + ActionVectorEntry.LocationName + " forms new service ";
15718  TimeStr = GetRepeatHeadCode(41, ActionVectorEntry.OtherHeadCode, y + 1, IncDigits) + " at " +
15719  Utilities->Format96HHMM(GetRepeatTime(14, ActionVectorEntry.EventTime, y, IncMinutes));
15720  // y+1 because it's the NEXT service repeat number that is relevant
15721  }
15722  else if((ActionVectorEntry.Command == "Fns-sh") && (y >= (TrainDataEntry.NumberOfTrains - 1)))
15723  {
15724  PartStr = "At " + ActionVectorEntry.LocationName + " forms new service";
15725  TimeStr = ActionVectorEntry.NonRepeatingShuttleLinkHeadCode + " at " + Utilities->Format96HHMM
15726  (GetRepeatTime(15, ActionVectorEntry.EventTime, y, IncMinutes));
15727  }
15728  else if((ActionVectorEntry.Command == "Frh-sh") && (y < (TrainDataEntry.NumberOfTrains - 1)))
15729  {
15730  PartStr = "At " + ActionVectorEntry.LocationName + " forms new service";
15731  TimeStr = GetRepeatHeadCode(43, ActionVectorEntry.OtherHeadCode, y + 1, IncDigits) + " at " +
15732  Utilities->Format96HHMM(GetRepeatTime(16, ActionVectorEntry.EventTime, y, IncMinutes));
15733  // y+1 because it's the NEXT service repeat number that is relevant
15734  }
15735  else if((ActionVectorEntry.Command == "Frh-sh") && (y >= (TrainDataEntry.NumberOfTrains - 1)))
15736  {
15737  PartStr = "Terminates shuttle service at " + ActionVectorEntry.LocationName;
15738  // only used in chronological tt
15739  TimeStr = "End at " + Utilities->Format96HHMM(GetRepeatTime(28, ActionVectorEntry.EventTime, y, IncMinutes));
15740  // the "End at " is stripped out of the chronological tt but displayed in the traditional tt
15741  }
15742  else if(ActionVectorEntry.Command == "Frh")
15743  {
15744  PartStr = "Terminates at " + ActionVectorEntry.LocationName;
15745  // need here to examine the time of the preceding entry, may be ArrivalTime if TimeLoc, or EventTime otherwise
15746  if(z > 0)
15747  // should be for finish entry but include check for safety
15748  {
15749  if(ActionVector.at(z - 1).EventTime != TDateTime(-1))
15750  {
15751  TimeStr = Utilities->Format96HHMM(GetRepeatTime(30, ActionVector.at(z - 1).EventTime, y, IncMinutes));
15752  }
15753  else if(ActionVector.at(z - 1).ArrivalTime != TDateTime(-1))
15754  {
15755  TimeStr = Utilities->Format96HHMM(GetRepeatTime(31, ActionVector.at(z - 1).ArrivalTime, y, IncMinutes));
15756  }
15757  else
15758  {
15759  TimeStr = " "; // shouldn't ever get here
15760  }
15761  }
15762  }
15763  else if(ActionVectorEntry.Command == "Fer")
15764  {
15765  AnsiString AllowedExits;
15766  PartStr = "Exits railway" + GetExitLocationAndAt(0, ActionVectorEntry.ExitList, AllowedExits) + AllowedExits;
15767  TimeStr = Utilities->Format96HHMM(GetRepeatTime(18, ActionVectorEntry.EventTime, y, IncMinutes));
15768  }
15769  else if(ActionVectorEntry.Command == "Fjo")
15770  {
15771  PartStr = "At " + ActionVectorEntry.LocationName + " joins";
15772  TimeStr = GetRepeatHeadCode(44, ActionVectorEntry.OtherHeadCode, y, IncDigits) + " at " +
15773  Utilities->Format96HHMM(GetRepeatTime(19, ActionVectorEntry.EventTime, y, IncMinutes));
15774  }
15775  }
15776  else if(ActionVectorEntry.SequenceType == SequTypeForRepeatEntry)
15777  {
15778  continue; // no entry needed for a repeat
15779  }
15780  OneTTEntry.Action = PartStr;
15781  OneTTEntry.Time = TimeStr;
15782  OneTTTrain.OneFormattedTrainVector.push_back(OneTTEntry);
15783  // one per action
15784  }
15785  OneTTLine.OneCompleteFormattedTrainVector.push_back(OneTTTrain);
15786  // one per repeat
15787  }
15788  AllTTTrains->push_back(OneTTLine); // one per repeating train
15789  }
15790  // AllTTTrains vector now complete
15791 
15792  std::ofstream TTFile(TTFileName.c_str()); //formatted timetable
15793 
15794  if(TTFile == 0)
15795  {
15796  StopTTClockMessage(64, "Formatted timetable file failed to open - can't be created");
15797  delete AllTTTrains;
15798  Utilities->CallLogPop(1567);
15799  return;
15800  }
15801 /* formatted timetable types
15802  class TOneTrainFormattedEntry
15803  {
15804  AnsiString Action;//includes location if relevant
15805  AnsiString Time;
15806  };
15807 
15808  typedef std::vector<TOneTrainFormattedEntry> TOneFormattedTrainVector;
15809 
15810  class TOneCompleteFormattedTrain//headcode + list of actions
15811  {
15812  public:
15813  AnsiString HeadCode;
15814  TOneFormattedTrainVector OneFormattedTrainVector;
15815  };
15816 
15817  typedef std::vector<TOneCompleteFormattedTrain> TOneCompleteFormattedTrainVector;//list af all repeats
15818 
15819  class TTrainFormattedInformation//contains all information for a single TT entry (including repeats)
15820  {
15821  public:
15822  AnsiString Header;//description, mass, power, brake rate etc
15823  int NumberOfTrains;// number of repeats + 1
15824  TOneCompleteFormattedTrainVector OneCompleteFormattedTrainVector;//list af all repeats
15825  };
15826 
15827  typedef std::vector<TTrainFormattedInformation> TAllFormattedTrains;//all timetable in formatted form
15828  //end of formatted timetable types
15829 */
15830 
15831  // new layout using multiple rows
15832  TTFile << TableTitle.c_str() << '\n' << '\n';
15833  for(unsigned int x = 0; x < AllTTTrains->size(); x++)
15834  {
15835  TTFile << AllTTTrains->at(x).Header.c_str();
15836  TTFile << '\n';
15837  TTFile << ','; // for the blank line above the action list
15838  for(int y = 0; y < AllTTTrains->at(x).NumberOfTrains; y++) // number of repeating trains
15839  {
15840  if(y < (AllTTTrains->at(x).NumberOfTrains - 1))
15841  {
15842  TTFile << AllTTTrains->at(x).OneCompleteFormattedTrainVector.at(y).HeadCode.c_str() << ',';
15843  }
15844  else
15845  {
15846  TTFile << AllTTTrains->at(x).OneCompleteFormattedTrainVector.at(y).HeadCode.c_str();
15847  }
15848  }
15849  TTFile << '\n' << '\n';
15850 
15851  for(unsigned int z = 0; z < AllTTTrains->at(x).OneCompleteFormattedTrainVector.at(0).OneFormattedTrainVector.size(); z++)
15852  {
15853  TTFile << AllTTTrains->at(x).OneCompleteFormattedTrainVector.at(0).OneFormattedTrainVector.at(z).Action.c_str() << ',';
15854  for(int y = 0; y < AllTTTrains->at(x).NumberOfTrains; y++) // number of repeating trains
15855  {
15856  if(y < (AllTTTrains->at(x).NumberOfTrains - 1))
15857  {
15858  TTFile << AllTTTrains->at(x).OneCompleteFormattedTrainVector.at(y).OneFormattedTrainVector.at(z).Time.c_str() << ',';
15859  }
15860  else
15861  {
15862  TTFile << AllTTTrains->at(x).OneCompleteFormattedTrainVector.at(y).OneFormattedTrainVector.at(z).Time.c_str();
15863  }
15864  }
15865  TTFile << '\n';
15866  }
15867  TTFile << '\n' << '\n';
15868  }
15869 
15870  TTFile.close();
15871 
15872  AnsiString TTFileName2 = TDateTime::CurrentDateTime().FormatString("dd-mm-yyyy hh.nn.ss");
15873 
15874  TTFileName2 = CurDir + "\\Formatted timetables\\Timetable " + TTFileName2 + "; " + RailwayTitle + "; " + TimetableTitle + ".txt";
15875 
15876  std::ofstream TTFile2(TTFileName2.c_str()); //chronological timetable
15877 
15878  if(TTFile2 == 0)
15879  {
15880  StopTTClockMessage(67, "Chronological timetable file failed to open - can't be created");
15881  delete AllTTTrains;
15882  Utilities->CallLogPop(1710);
15883  return;
15884  }
15885  typedef std::multimap<AnsiString, AnsiString>TAnsiMultiMap;
15886  std::multimap<AnsiString, AnsiString>::iterator AMMIT;
15887  std::pair<AnsiString, AnsiString>AnsiMultiMapEntry;
15888 
15889  TAnsiMultiMap *TAMM = new TAnsiMultiMap;
15890  LastTTTime = ""; //records the very last time in the timetable - used in analysis file for Frh entries
15891 
15892  // multimap of AnsiStrings with TimeString as key (to sort automatically)
15893 
15894  TTFile2 << TableTitle.c_str() << '\n' << '\n';
15895  for(unsigned int x = 0; x < AllTTTrains->size(); x++)
15896  {
15897  for(int y = 0; y < AllTTTrains->at(x).NumberOfTrains; y++) // number of repeating trains
15898  {
15899  for(unsigned int z = 0; z < AllTTTrains->at(x).OneCompleteFormattedTrainVector.at(y).OneFormattedTrainVector.size(); z++)
15900  {
15901  bool GiveMessagesFalse = false;
15902  AnsiString TimeString = AllTTTrains->at(x).OneCompleteFormattedTrainVector.at(y).OneFormattedTrainVector.at(z).Time;
15903  AnsiString HeadCodeString = AllTTTrains->at(x).OneCompleteFormattedTrainVector.at(y).HeadCode;
15904  AnsiString ActionString = AllTTTrains->at(x).OneCompleteFormattedTrainVector.at(y).OneFormattedTrainVector.at(z).Action;
15905  if(CheckHeadCodeValidity(11, GiveMessagesFalse, TimeString.SubString(1, 4)))
15906  // 'NXNN at HH:MM' (will return true if H/C as integ check passed)
15907  {
15908  // fails for HH:MM because of ':' or 'End at HH:MM' because of ' '
15909  AnsiString OtherHeadCode = TimeString.SubString(1, 4);
15910  TimeString = TimeString.SubString(9, 5);
15911  ActionString += " " + OtherHeadCode;
15912  }
15913  if(TimeString.SubString(1, 7) == "End at ")
15914  // for Frh-sh final entry
15915  {
15916  TimeString = TimeString.SubString(8, 5);
15917  }
15918  AnsiString OneLine = TimeString + ' ' + HeadCodeString + ' ' + ActionString + '\n';
15919  AnsiMultiMapEntry.first = TimeString;
15920  AnsiMultiMapEntry.second = OneLine;
15921  TAMM->insert(AnsiMultiMapEntry);
15922  }
15923  }
15924  }
15925 
15926  for(AMMIT = TAMM->begin(); AMMIT != TAMM->end(); AMMIT++)
15927  {
15928  TTFile2 << (AMMIT->second).c_str();
15929  }
15930  delete AllTTTrains;
15931  delete TAMM;
15932  TTFile2.close();
15933  Utilities->CallLogPop(1580);
15934 }
15935 
15936 // ---------------------------------------------------------------------------
15937 
15938 bool TTrainController::CreateTTAnalysisFile(int Caller, AnsiString RailwayTitle, AnsiString TimetableTitle, AnsiString CurDir, bool ArrChecked, bool DepChecked,
15939  bool AtLocChecked, bool DirChecked, int ArrRange, int DepRange)
15940 {
15941 
15942  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CreateTTAnalysisFile");
15943  bool AnalysisError = false;
15944  AnsiString SequenceLog = "SequenceLog\n";
15945 
15946 /* Double crosslink (shuttle) table:
15947 
15948 Command Format OtherHead NonRepeating- LinkTrain- NonRepeating- Decsription
15949  Code ShuttleLink- EntryPtr ShuttleLink-
15950  HeadCode EntryPtr
15951 
15952 Snt-sh SNTShuttle Y (rtn shuttle) N Y (rtn sh) N Simple shuttle - no feeder service
15953 Frh-sh TimeCmdHeadCode Y (outwd shuttle) N Y (outwd sh) N Simple shuttle - no finishing service
15954 F-nshs FNSNonRepeatToShuttle N (shld be Y for outwd shuttle) Y (shld be N) Y (correct) N (correct) Feeder service link to shuttle
15955 Sns-sh SNSShuttle Y (rtn shuttle) Y (feeder) Y (rtn) Y (fdr) Shuttle link from feeder service
15956 Sns-fsh SNSNonRepeatFromShuttle N (shld be Y for rtn shuttle) Y (shld be N) Y (correct) N (correct) Finishing service link from shuttle
15957 Fns-sh FSHNewService Y (outwd shuttle) Y (finishing) Y (outwd sh) Y (finish) Shuttle link to finishing service
15958 
15959 Note: Any shuttle start can have any finish - feeder and finish, neither, feeder but no finish & vice versa.
15960 */
15961 
15962  try
15963  {
15964  //New section at v2.5.0 for tt conflict analysis
15965  /*
15966  typedef std::list<AnsiString> TServiceCallingLocsList;
15967  typedef std::map<AnsiString, TServiceCallingLocsList> TAllServiceCallingLocsMap;
15968 
15970  struct TLocServiceTimes
15971  {
15972  AnsiString Location;
15973  AnsiString ServiceAndRepeatNum;
15974  AnsiString AtLocTime;
15975  AnsiString ArrTime;
15976  AnsiString DepTime;
15977  AnsiString FrhMarker;
15978  };
15979  typedef std::vector<TLocServiceTimes> TLocServiceTimesVector;
15980  */
15981 
15982  //first have to check through all the services and give each one a unique name, or the analysis won't recognise differences between services that have the same reference
15983  //to do that need a new TrainDataVector as don't want to change anything in the original. TrainDataVectorCopy is used for building AllServiceCallingLocsMap & LocServiceTimesVector
15984 
15985  TTrainDataVector TrainDataVectorCopy = TrainDataVector; //don't need it on heap as TrainController is on the heap. Didn't need others in CreatFormattedTimetables but leave as is.
15986  TTrainDataVector::iterator TDVIt, TDVCopyIt;
15987  int Suffix = 0;
15988  int IteratorNumber = 0;
15989  AnsiString AnsiSuffix = "";
15990  for(TDVIt = TrainDataVector.begin(); TDVIt != TrainDataVector.end() - 1; TDVIt++)
15991  {
15992  IteratorNumber++; //first value in loop is 1
15993  Suffix = 0;
15994  for(TDVCopyIt = TrainDataVectorCopy.begin() + IteratorNumber; TDVCopyIt != TrainDataVectorCopy.end(); TDVCopyIt++)
15995  {
15996  if(TDVCopyIt->ServiceReference == TDVIt->ServiceReference)
15997  {
15998  Suffix++; //first value is 1
15999  AnsiSuffix = AnsiString(Suffix);
16000  TDVCopyIt->ServiceReference = TDVIt->ServiceReference + "/" + AnsiSuffix;
16001  }
16002  }
16003  }
16004  SequenceLog += "1\n";
16005  //build AllServiceCallingLocsMap, it only uses the base service reference (with /1, /2 etc suffixes) as later times are calculated from the repeat number
16006  TServiceCallingLocsList ServiceCallingLocsList;
16007  std::pair<AnsiString, TServiceCallingLocsList> AllServiceCallingLocsEntry;
16008  for(unsigned int x = 0; x < TrainDataVectorCopy.size(); x++)
16009  {
16010  const TTrainDataEntry &TrainDataEntry = TrainDataVectorCopy.at(x);
16011  const TActionVector &ActionVector = TrainDataEntry.ActionVector;
16012  AllServiceCallingLocsEntry.first = TrainDataEntry.ServiceReference;
16013  ServiceCallingLocsList.clear();
16014  if(ActionVector.empty())
16015  {
16016  continue;
16017  }
16018  if(ActionVector.at(0).SignallerControl)
16019  {
16020  continue;
16021  }
16022  for(unsigned int z = 0; z < ActionVector.size(); z++)
16023  {
16024  TActionVectorEntry AVE = ActionVector.at(z);
16025  if(AVE.FormatType == StartNew)
16026  {
16027  if(AVE.LocationType == AtLocation) //located Snt
16028  {
16029  ServiceCallingLocsList.push_back(AVE.LocationName);
16030  }
16031  else //unlocated Snt (could be entering at continuation)
16032  {
16034  if(TE.ActiveTrackElementName != "")
16035  {
16036  ServiceCallingLocsList.push_back(TE.ActiveTrackElementName);
16037  }
16038  else
16039  {
16040  int HLoc = TE.HLoc;
16041  int VLoc = TE.VLoc;
16042  AnsiString HString;
16043  AnsiString VString;
16044  if(HLoc < 0)
16045  {
16046  HString = AnsiString('N') + AnsiString(HLoc).SubString(2, AnsiString(HLoc).Length() - 1); //strip off '-'
16047  }
16048  else
16049  {
16050  HString = AnsiString(HLoc);
16051  }
16052  if(VLoc < 0)
16053  {
16054  VString = AnsiString('N') + AnsiString(VLoc).SubString(2, AnsiString(VLoc).Length() - 1); //strip off '-'
16055  }
16056  else
16057  {
16058  VString = AnsiString(VLoc);
16059  }
16060  ServiceCallingLocsList.push_back(HString + '-' + VString);
16061  }
16062  }
16063  }
16064  else if(AVE.SequenceType == Start) //other start entries, all located
16065  {
16066  ServiceCallingLocsList.push_back(AVE.LocationName);
16067  }
16068  else if(AVE.FormatType == TimeLoc) //z must be > 0
16069  {
16070  if(ServiceCallingLocsList.back() != AVE.LocationName)
16071  {
16072  ServiceCallingLocsList.push_back(AVE.LocationName); //may be listed twice in succession so only want one entry
16073  }
16074  }
16075  else if(AVE.FormatType == PassTime)
16076  {
16077  ServiceCallingLocsList.push_back(AVE.LocationName);
16078  }
16079  else if(AVE.FormatType == TimeTimeLoc)
16080  {
16081  ServiceCallingLocsList.push_back(AVE.LocationName);
16082  }
16083  else if(AVE.Command == "cdt") //list if not next to start or finish
16084  {
16085  if(ActionVector.at(z-1).SequenceType == Start)
16086  {
16087  continue;
16088  }
16089  else if(ActionVector.at(z+1).SequenceType == Finish) //although deal with Fer entries cdt (train stopped) can't precede FER (train moving)
16090  {
16091  continue;
16092  }
16093  else
16094  {
16095  AnsiString TimeString = Utilities->Format96HHMM(AVE.EventTime);
16096  ServiceCallingLocsList.push_back("%%%" + TimeString); //%%% is a marker - unlikely that any locations will begin with this & easy to check to identify a time
16097  }
16098  }
16099  else if(AVE.FormatType == ExitRailway) //Fer
16100  {
16101  TTrackElement TE = Track->TrackElementAt(995, AVE.ExitList.front());
16102  AnsiString LName = TE.ActiveTrackElementName;
16103  if(LName != "")
16104  {
16105  ServiceCallingLocsList.push_back(LName);
16106  }
16107  else
16108  {
16109  int HLoc = TE.HLoc;
16110  int VLoc = TE.VLoc;
16111  AnsiString HString;
16112  AnsiString VString;
16113  if(HLoc < 0)
16114  {
16115  HString = AnsiString('N') + AnsiString(HLoc).SubString(2, AnsiString(HLoc).Length() - 1); //strip off '-'
16116  }
16117  else
16118  {
16119  HString = AnsiString(HLoc);
16120  }
16121  if(VLoc < 0)
16122  {
16123  VString = AnsiString('N') + AnsiString(VLoc).SubString(2, AnsiString(VLoc).Length() - 1); //strip off '-'
16124  }
16125  else
16126  {
16127  VString = AnsiString(VLoc);
16128  }
16129  ServiceCallingLocsList.push_back(HString + '-' + VString);
16130  }
16131  }
16132  }
16133  AllServiceCallingLocsEntry.second = ServiceCallingLocsList;
16134  AllServiceCallingLocsMap.insert(AllServiceCallingLocsEntry);
16135  }
16136  //AllServiceCallingLocsMap built
16137  SequenceLog += "2\n";
16138  //test validity of AllServiceCallingLocsMap
16139 /*
16140  AnsiString TestFile = CurDir + "\\Formatted timetables\\TestFile; " + RailwayTitle + "; " + TimetableTitle + ".txt";
16141  std::ofstream Test(TestFile.c_str());
16142 
16143  if(TestFile == 0)
16144  {
16145  ShowMessage("TestFile failed to open - can't be created");
16146  Utilities->CallLogPop();
16147  return false;
16148  }
16149 
16150  for(TAllServiceCallingLocsMap::iterator ASCLIt = AllServiceCallingLocsMap.begin(); ASCLIt != AllServiceCallingLocsMap.end(); ASCLIt++)
16151  {
16152  Test << ASCLIt->first << '\n'; //service ref
16153  for(TServiceCallingLocsList::iterator SCLIt = ASCLIt->second.begin(); SCLIt != ASCLIt->second.end(); SCLIt++)
16154  {
16155  Test << *SCLIt << '\n';
16156  }
16157  Test << "\n\n";
16158  }
16159  Test.close();
16160  Utilities->CallLogPop();
16161  return true;
16162 */
16163 
16164  //initialise variables before calc LastTTTime & build LocServiceTimesVector
16165  if(TrainDataVector.empty())
16166  {
16167  ShowMessage("Unable to create a program-readable timetable - please check the timetable file validity");
16168  Utilities->CallLogPop(2209);
16169  return(false);
16170  }
16171  TLocServiceTimes TLSTEntry;
16172  TLocServiceTimesVector LocServiceTimesVector; //will be on heap as TrainController is on the heap
16173  bool NumPlatsAtThisLocCalculated = false, ArrivalsPrinted = false, DeparturesPrinted = false, AtLocsPrinted = false;
16174  AnsiString PreviousService = "", PreviousServiceAndRepeatNumTotalOutput = "", BasicTime = "", MinuteString = "", LastAnsiTime = "";
16175  int NumTrains = 0, NumPlats = 0, LastFrhCount = 0, FrhCount = 0, NumTrainsAtLoc = 0;
16176  LastTTTime = "";
16177  SequenceLog += "3\n";
16178  //calculate LastTTTime
16179  for(unsigned int x = 0; x < TrainDataVectorCopy.size(); x++)
16180  {
16181  TTrainDataEntry &TrainDataEntry = TrainDataVectorCopy.at(x);
16182  TActionVector &ActionVector = TrainDataEntry.ActionVector;
16183  TActionVectorIterator AVLast = ActionVector.end() - 1; //points to last entry
16184  TDateTime LastTDTime;
16185  int IncMinutes = 0;
16186  NumTrains = TrainDataEntry.NumberOfTrains;
16187  if(ActionVector.empty())
16188  {
16189  continue;
16190  }
16191  if(ActionVector.at(0).SignallerControl)
16192  {
16193  continue;
16194  }
16195  if(AVLast->FormatType == Repeat)
16196  {
16197  IncMinutes = ActionVector.at(ActionVector.size() - 1).RearStartOrRepeatMins;
16198  AVLast--; //now points to the command before the repeat
16199  }
16200  if(AVLast->FormatType == FinRemHere) //not 'else if' as may have both a repeat and an Frh
16201  {
16202  AVLast--; //points to last timed entry
16203  }
16204  //here AVLast points to last entry with a time
16205  if(AVLast->ArrivalTime != TDateTime(-1))
16206  {
16207  LastTDTime = AVLast->ArrivalTime;
16208  }
16209  else if(AVLast->EventTime != TDateTime(-1)) //can't be a departure time
16210  {
16211  LastTDTime = AVLast->EventTime;
16212  }
16213  else
16214  {
16215  continue; //shouldn't ever reach here but if do then skip this service
16216  }
16217  if(NumTrains == 1)
16218  {
16219  LastAnsiTime = Utilities->Format96HHMM(LastTDTime);
16220  }
16221  else
16222  {
16223  LastAnsiTime = Utilities->Format96HHMM(GetRepeatTime(59, LastTDTime, NumTrains - 1, IncMinutes));
16224  }
16225  if(LastAnsiTime > LastTTTime)
16226  {
16227  LastTTTime = LastAnsiTime;
16228  }
16229  }
16230  SequenceLog += "4\n";
16231 //build LocServiceTimesVector
16232 
16233 /*
16234  struct TLocServiceTimes
16235  {
16236  AnsiString Location;
16237  AnsiString ServiceAndRepeatNum;
16238  AnsiString AtLocTime;
16239  AnsiString ArrTime;
16240  AnsiString DepTime;
16241  AnsiString FrhMarker;
16242  };
16243  typedef std::vector<TLocServiceTimes> TLocServiceTimesVector;
16244 
16245 This works as follows:
16246 ServiceAndRepeatNum is taken from the TrainDataVector as it is the same for all actionvector entries
16247 Location is taken from ActionVectorEntry.LocationName if there is one, or from the H & V locations if not (e.g. at an unnamed Fer)
16248 AtLocTime is always entered either on its own or with ArrTime or DepTime as appropriate
16249 
16250 Every action for every train is examined and times entered as follows:-
16251 a) a located Snt: entry time becomes the AtLocTime, and all subsequent minutes entered too up to but not including a departure or a finish
16252 b) an unlocated Snt: entry time becomes DepTime
16253 c) all other start entries: entry time becomes AtLoc, and all subsequent minutes entered too up to but not including a departure or a finish
16254 d) TimeLoc Arr: entry time becomes ArrTime, and all subsequent minutes entered too up to but not including a departure or a finish
16255 e) TimeLoc Dep: entry time becomes DepTime, checks if DepTime same as earlier ArrTime and if so all times go in as one entry
16256 f) TimeTimeLoc: Arrival time entered as ArrTime, a check if Arr & Dep same and if so go in as one entry, else all minutes between entered as AtLocs then DepTime
16257 g) ExitRailway (Fer): check if located and use LocationName if so. else use H & V positions, time becomes AtLocTime
16258 h) Frh: use the earlier vector time as the AtLocTime and set FrhMarker, and enter all minutes to end of timetable as AtLocs
16259 i) Frh-sh: for the last train use time as AtLocTime, set FrhMarker, and enter all minutes to end of timetable as AtLocs
16260 j) all other finish entries (all link to another service) are ignored as will be listed for the linked service
16261 */
16262  for(unsigned int x = 0; x < TrainDataVectorCopy.size(); x++)
16263  {
16264  const TTrainDataEntry &TrainDataEntry = TrainDataVectorCopy.at(x);
16265  const TActionVector &ActionVector = TrainDataEntry.ActionVector;
16266  AnsiString ServiceRef = TrainDataEntry.ServiceReference;
16267  int IncMinutes = 0;
16268  NumTrains = TrainDataEntry.NumberOfTrains;
16269  if(ActionVector.empty())
16270  {
16271  continue;
16272  }
16273  if(ActionVector.at(0).SignallerControl)
16274  {
16275  continue;
16276  }
16277  if(ActionVector.at(ActionVector.size() - 1).FormatType == Repeat)
16278  {
16279  IncMinutes = ActionVector.at(ActionVector.size() - 1).RearStartOrRepeatMins;
16280  }
16281  for(int y = 0; y < NumTrains; y++)
16282  {
16283  if(NumTrains == 1)
16284  {
16285  TLSTEntry.ServiceAndRepeatNum = ServiceRef;
16286  }
16287  else if(y == 0)
16288  {
16289  TLSTEntry.ServiceAndRepeatNum = ServiceRef + " (First service)";
16290  }
16291  else
16292  {
16293  TLSTEntry.ServiceAndRepeatNum = ServiceRef + " (Repeat " + AnsiString(y) + ")";
16294  }
16295  for(unsigned int z = 0; z < ActionVector.size(); z++)
16296  {
16297  TActionVectorEntry AVE = ActionVector.at(z);
16298  TLSTEntry.AtLocTime = "";
16299  TLSTEntry.ArrTime = "";
16300  TLSTEntry.DepTime = "";
16301  TLSTEntry.Location = "";
16302  TLSTEntry.FrhMarker = "";
16303 
16304  if(AVE.FormatType == StartNew) //Snt only
16305  {
16306  if(AVE.LocationType == AtLocation) //located Snt, class time as AtLocTime
16307  {
16308  TLSTEntry.Location = AVE.LocationName;
16309  TLSTEntry.AtLocTime = Utilities->Format96HHMM(GetRepeatTime(58, AVE.EventTime, y, IncMinutes));
16310  LocServiceTimesVector.push_back(TLSTEntry);
16311 
16312  //now look forwards until find a departure or a Fns, Fns-sh etc & add in all the minutes up to but not including the dep or finish times
16313  AnsiString IncTime = "", FoundStopTime = ""; //these handled in later checks
16314  for(unsigned int a = z + 1; a < ActionVector.size(); a++)
16315  {
16316  if(ActionVector.at(a).FormatType == TimeLoc) //must be a departure
16317  {
16318  FoundStopTime = Utilities->Format96HHMM(GetRepeatTime(62, ActionVector.at(a).DepartureTime, y, IncMinutes));
16319  break;
16320  }
16321  if(ActionVector.at(a).SequenceType == Finish) //finish catered in a later test
16322  {
16323  FoundStopTime = Utilities->Format96HHMM(GetRepeatTime(63, ActionVector.at(a).EventTime, y, IncMinutes));
16324  break;
16325  }
16326  }
16327  if(FoundStopTime == "")
16328  {
16329  throw Exception("Failure to determine FoundStopTime for located Snt");
16330  }
16331  int WhileCount = 0;
16332  while(true)
16333  {
16334  //add minutes until reach FoundStopTime but don't add that time
16335  WhileCount++;
16336  IncTime = Utilities->IncrementAnsiTimeOneMinute(TLSTEntry.AtLocTime);
16337  TLSTEntry.AtLocTime = IncTime; //all entered times will be AtLocs
16338  TLSTEntry.DepTime = "";
16339  TLSTEntry.ArrTime = "";
16340  if(IncTime >= FoundStopTime) //don't add that time
16341  {
16342  break;
16343  }
16344  LocServiceTimesVector.push_back(TLSTEntry);
16345  if(WhileCount > 2000)
16346  {
16347  throw Exception("While loop failed to break in 2000 loops for located Snt");
16348  }
16349  }
16350  }
16351  else //unlocated Snt, use the EventTime as DepTime for this vector
16352  {
16354  if(TE.ActiveTrackElementName != "")
16355  {
16356  TLSTEntry.Location = TE.ActiveTrackElementName;
16357  }
16358  else
16359  {
16360  int HLoc = TE.HLoc;
16361  int VLoc = TE.VLoc;
16362  AnsiString HString;
16363  AnsiString VString;
16364  if(HLoc < 0)
16365  {
16366  HString = AnsiString('N') + AnsiString(HLoc).SubString(2, AnsiString(HLoc).Length() - 1); //strip off '-'
16367  }
16368  else
16369  {
16370  HString = AnsiString(HLoc);
16371  }
16372  if(VLoc < 0)
16373  {
16374  VString = AnsiString('N') + AnsiString(VLoc).SubString(2, AnsiString(VLoc).Length() - 1); //strip off '-'
16375  }
16376  else
16377  {
16378  VString = AnsiString(VLoc);
16379  }
16380  TLSTEntry.Location = HString + '-' + VString;
16381  }
16382  TLSTEntry.DepTime = Utilities->Format96HHMM(GetRepeatTime(49, AVE.EventTime, y, IncMinutes));
16383  TLSTEntry.AtLocTime = TLSTEntry.DepTime;
16384  LocServiceTimesVector.push_back(TLSTEntry);
16385  }
16386  }
16387 
16388  else if(AVE.SequenceType == Start) //other start entries, all located
16389  {
16390  TLSTEntry.Location = AVE.LocationName;
16391  TLSTEntry.AtLocTime = Utilities->Format96HHMM(GetRepeatTime(50, AVE.EventTime, y, IncMinutes));
16392  LocServiceTimesVector.push_back(TLSTEntry);
16393  //now look forwards until find a departure or a Fns, Fns-sh etc & add in all the minutes up to but not including the dep or finish times
16394  AnsiString IncTime = "", FoundStopTime = ""; //these handled in other checks
16395  for(unsigned int a = z + 1; a < ActionVector.size(); a++)
16396  {
16397  if(ActionVector.at(a).FormatType == TimeLoc) //must be a departure
16398  {
16399  FoundStopTime = Utilities->Format96HHMM(GetRepeatTime(64, ActionVector.at(a).DepartureTime, y, IncMinutes));
16400  break;
16401  }
16402  if(ActionVector.at(a).SequenceType == Finish) //finish catered in a later test
16403  {
16404  FoundStopTime = Utilities->Format96HHMM(GetRepeatTime(65, ActionVector.at(a).EventTime, y, IncMinutes));
16405  break;
16406  }
16407  }
16408  if(FoundStopTime == "")
16409  {
16410  throw Exception("Failure to determine FoundStopTime for SequenceType == Start");
16411  }
16412  int WhileCount = 0;
16413  while(true)
16414  {
16415  //add minutes until reach FoundStopTime but don't add that time
16416  WhileCount++;
16417  IncTime = Utilities->IncrementAnsiTimeOneMinute(TLSTEntry.AtLocTime);
16418  TLSTEntry.AtLocTime = IncTime; //all entered times will be AtLocs
16419  TLSTEntry.DepTime = "";
16420  TLSTEntry.ArrTime = "";
16421  if(IncTime >= FoundStopTime) //don't add that time
16422  {
16423  break;
16424  }
16425  LocServiceTimesVector.push_back(TLSTEntry);
16426  if(WhileCount > 2000)
16427  {
16428  throw Exception("While loop failed to break in 2000 loops for SequenceType == Start");
16429  }
16430  }
16431  }
16432 
16433  else if(AVE.FormatType == TimeLoc) //could be arr or dep, if arrival add in all mins to the departure or finish
16434  {
16435  TLSTEntry.Location = AVE.LocationName;
16436  if(AVE.ArrivalTime > TDateTime(-1)) //one or other set, not both, in this case arrival
16437  {
16438  bool SkipAddingMinutes = false;
16439  TLSTEntry.ArrTime = Utilities->Format96HHMM(GetRepeatTime(51, AVE.ArrivalTime, y, IncMinutes));
16440  TLSTEntry.AtLocTime = TLSTEntry.ArrTime;
16441  LocServiceTimesVector.push_back(TLSTEntry); //Arr and AtLoc added (may be popped if dep time found to be same at next TimeLoc)
16442  //now look forwards until find a departure or a Fns, Fns-sh etc & add in all the minutes up to but not including the dep or finish times
16443  AnsiString IncTime = "", FoundStopTime = ""; //these handled in other checks
16444  for(unsigned int a = z + 1; a < ActionVector.size(); a++)
16445  {
16446  if(ActionVector.at(a).FormatType == TimeLoc) //must be a departure
16447  {
16448  FoundStopTime = Utilities->Format96HHMM(GetRepeatTime(66, ActionVector.at(a).DepartureTime, y, IncMinutes));
16449  break;
16450  }
16451  if(ActionVector.at(a).SequenceType == Finish) //finish catered in a later test
16452  {
16453  FoundStopTime = Utilities->Format96HHMM(GetRepeatTime(67, ActionVector.at(a).EventTime, y, IncMinutes));
16454  if((a <= (z + 2)) && (FoundStopTime == TLSTEntry.ArrTime) && ((ActionVector.at(a).LinkedTrainEntryPtr > 0) || (ActionVector.at(a).NonRepeatingShuttleLinkEntryPtr > 0)))
16455  //finish immediately after arrival at same time, and a forward linked service. Added at v2.6.0 to prevent two linked trains being listed at same location
16456  //changed to a <= (z + 2) at 2.10.0 to allow for a cdt before change of service
16457  {
16458  LocServiceTimesVector.pop_back(); //pop the entry as the linked train will be listed at the relevant time and don't want to list both
16459  SkipAddingMinutes = true;
16460  }
16461  break;
16462  }
16463  }
16464  if(FoundStopTime == "")
16465  {
16466  throw Exception("Failure to determine FoundStopTime for SequenceType == Start");
16467  }
16468  if(!SkipAddingMinutes)
16469  {
16470  int WhileCount = 0;
16471  while(true)
16472  {
16473  //add minutes until reach FoundStopTime but don't add that time
16474  WhileCount++;
16475  IncTime = Utilities->IncrementAnsiTimeOneMinute(TLSTEntry.AtLocTime);
16476  TLSTEntry.AtLocTime = IncTime; //all entered times will be AtLocs
16477  TLSTEntry.DepTime = "";
16478  TLSTEntry.ArrTime = "";
16479  if(IncTime >= FoundStopTime) //don't add that time
16480  {
16481  break;
16482  }
16483  LocServiceTimesVector.push_back(TLSTEntry);
16484  if(WhileCount > 2000)
16485  {
16486  throw Exception("While loop failed to break in 2000 loops for SequenceType == Start");
16487  }
16488  }
16489  }
16490  }
16491  else if(AVE.DepartureTime > TDateTime(-1)) //need to check if the arrival time (which should already be listed) is same and if so put all times on one line
16492  {
16493  TLSTEntry.DepTime = Utilities->Format96HHMM(GetRepeatTime(52, AVE.DepartureTime, y, IncMinutes));
16494  TLSTEntry.AtLocTime = TLSTEntry.DepTime;
16495  if((TLSTEntry.Location == LocServiceTimesVector.back().Location) && (TLSTEntry.ServiceAndRepeatNum == LocServiceTimesVector.back().ServiceAndRepeatNum)) //if not it's a new service
16496  {
16497  if(TLSTEntry.DepTime == LocServiceTimesVector.back().ArrTime)
16498  {
16499  TLSTEntry.ArrTime = LocServiceTimesVector.back().ArrTime;
16500  LocServiceTimesVector.pop_back();
16501  LocServiceTimesVector.push_back(TLSTEntry); //Arr, Dep and AtLoc added in place of earlier Arr entry.
16502  }
16503  else //just add the dep & atloc times
16504  {
16505  TLSTEntry.ArrTime = "";
16506  LocServiceTimesVector.push_back(TLSTEntry);
16507  }
16508  }
16509  else //just add the dep & atloc times
16510  {
16511  TLSTEntry.ArrTime = "";
16512  LocServiceTimesVector.push_back(TLSTEntry);
16513  }
16514  }
16515  }
16516 
16517  else if(AVE.FormatType == TimeTimeLoc)
16518  {
16519  TLSTEntry.Location = AVE.LocationName;
16520  if(AVE.ArrivalTime > TDateTime(-1)) //should be
16521  {
16522  TLSTEntry.ArrTime = Utilities->Format96HHMM(GetRepeatTime(53, AVE.ArrivalTime, y, IncMinutes));
16523  TLSTEntry.AtLocTime = TLSTEntry.ArrTime;
16524  }
16525  if(AVE.DepartureTime > TDateTime(-1)) //should be
16526  {
16527  TLSTEntry.DepTime = Utilities->Format96HHMM(GetRepeatTime(54, AVE.DepartureTime, y, IncMinutes));
16528  }
16529  if(TLSTEntry.ArrTime == TLSTEntry.DepTime)
16530  {
16531  LocServiceTimesVector.push_back(TLSTEntry);
16532  }
16533  else
16534  {
16535  AnsiString TempDepTime = TLSTEntry.DepTime; //save it temporarily
16536  TLSTEntry.DepTime = "";
16537  LocServiceTimesVector.push_back(TLSTEntry); //push just the arrival and AtLoc times
16538  TLSTEntry.ArrTime = ""; //done with this now
16539  while(TLSTEntry.AtLocTime < TempDepTime)
16540  {
16541  TLSTEntry.AtLocTime = Utilities->IncrementAnsiTimeOneMinute(TLSTEntry.AtLocTime);
16542  if(TLSTEntry.AtLocTime == TempDepTime)
16543  {
16544  TLSTEntry.DepTime = TempDepTime; //restore value
16545  LocServiceTimesVector.push_back(TLSTEntry); //push the AtLoc and Dep times - will finish loop after this
16546  }
16547  else
16548  {
16549  LocServiceTimesVector.push_back(TLSTEntry); //push the AtLoc time on its own
16550  }
16551  }
16552  }
16553  }
16554 
16555  else if(AVE.FormatType == PassTime) //added at v2.9.1
16556  { //adds 2 entries, 1st with PassTime as ArrTime and AtLocTime, 2nd with PassTime as AtLocTime & DepTime
16557  TLSTEntry.Location = AVE.LocationName;;
16558  TLSTEntry.AtLocTime = Utilities->Format96HHMM(GetRepeatTime(73, AVE.EventTime, y, IncMinutes));
16559  TLSTEntry.ArrTime = TLSTEntry.AtLocTime; //DepTime already set to null
16560  LocServiceTimesVector.push_back(TLSTEntry); //1st entry
16561  TLSTEntry.ArrTime = ""; //need to reset this to null
16562  TLSTEntry.DepTime = TLSTEntry.AtLocTime;
16563  LocServiceTimesVector.push_back(TLSTEntry); //2nd entry
16564  }
16565 
16566  else if(AVE.FormatType == ExitRailway) //Fer
16567  {
16568  TLSTEntry.AtLocTime = Utilities->Format96HHMM(GetRepeatTime(55, AVE.EventTime, y, IncMinutes));
16569  //don't know which exit will be used during operation so use the first in ExitList, if several with different names then will
16570  //be wrong, but can't guess from here & most will have same name
16571  AnsiString LName = Track->TrackElementAt(990, AVE.ExitList.front()).ActiveTrackElementName;
16572  if(LName != "")
16573  {
16574  TLSTEntry.Location = LName;
16575  }
16576  else
16577  {
16578  int HLoc = Track->TrackElementAt(991, AVE.ExitList.front()).HLoc;
16579  int VLoc = Track->TrackElementAt(992, AVE.ExitList.front()).VLoc;
16580  AnsiString HString;
16581  AnsiString VString;
16582  if(HLoc < 0)
16583  {
16584  HString = AnsiString('N') + AnsiString(HLoc).SubString(2, AnsiString(HLoc).Length() - 1); //strip off '-'
16585  }
16586  else
16587  {
16588  HString = AnsiString(HLoc);
16589  }
16590  if(VLoc < 0)
16591  {
16592  VString = AnsiString('N') + AnsiString(VLoc).SubString(2, AnsiString(VLoc).Length() - 1); //strip off '-'
16593  }
16594  else
16595  {
16596  VString = AnsiString(VLoc);
16597  }
16598  TLSTEntry.Location = HString + '-' + VString;
16599  }
16600  LocServiceTimesVector.push_back(TLSTEntry); //just use the exit time as AtLocTime
16601  }
16602 
16603  else if(AVE.FormatType == FinRemHere) //Frh, not Frh-sh, that dealt with next
16604  {
16605  AnsiString FrhTime;
16606  if(ActionVector.at(z - 1).ArrivalTime != TDateTime(-1))
16607  {
16608  FrhTime = Utilities->Format96HHMM(GetRepeatTime(56, ActionVector.at(z - 1).ArrivalTime, y, IncMinutes));
16609  }
16610  else if(ActionVector.at(z - 1).EventTime != TDateTime(-1))
16611  {
16612  FrhTime = Utilities->Format96HHMM(GetRepeatTime(57, ActionVector.at(z - 1).EventTime, y, IncMinutes));
16613  }
16614  TLSTEntry.AtLocTime = FrhTime; //use the last entry time as the first recorded time
16615  TLSTEntry.Location = AVE.LocationName;
16616  AnsiString IncTime = Utilities->IncrementAnsiTimeOneMinute(TLSTEntry.AtLocTime);
16617  TLSTEntry.FrhMarker = "Frh";
16618  LocServiceTimesVector.push_back(TLSTEntry);
16619  TLSTEntry.FrhMarker = "";
16620  //add all times from next minute to end of timetable
16621  while(IncTime <= LastTTTime)
16622  {
16623  TLSTEntry.AtLocTime = IncTime;
16624  LocServiceTimesVector.push_back(TLSTEntry);
16625  IncTime = Utilities->IncrementAnsiTimeOneMinute(IncTime);
16626  }
16627  }
16628 
16629  else if(AVE.Command == "Frh-sh") //do nothing if links to other shuttle but treat as Frh when remaining here
16630  {
16631  if(y == NumTrains - 1) //last repeat, it remains here when accessed for the last train
16632  {
16633  TLSTEntry.AtLocTime = Utilities->Format96HHMM(GetRepeatTime(68, AVE.EventTime, y, IncMinutes));
16634  TLSTEntry.Location = AVE.LocationName;
16635  AnsiString IncTime = Utilities->IncrementAnsiTimeOneMinute(TLSTEntry.AtLocTime);
16636  TLSTEntry.FrhMarker = "Frh";
16637  LocServiceTimesVector.push_back(TLSTEntry);
16638  TLSTEntry.FrhMarker = "";
16639  //add all times from next minute to end of timetable
16640  while(IncTime <= LastTTTime)
16641  {
16642  TLSTEntry.AtLocTime = IncTime;
16643  LocServiceTimesVector.push_back(TLSTEntry);
16644  IncTime = Utilities->IncrementAnsiTimeOneMinute(IncTime);
16645  }
16646  }
16647  }
16648 
16649  else if(AVE.SequenceType == Finish) //other finish types - all located & all link to another service
16650  {
16651  //nothing is done here as the entry will be listed at this time under the new service reference
16652  }
16653  }
16654  }
16655  }
16656  SequenceLog += "5\n";
16657  //now sort in location order
16658  std::sort(LocServiceTimesVector.begin(), LocServiceTimesVector.end(), &LocServiceTimesLocationSort); //LocServiceTimesLocationSort is a function pointer
16659  //LocServiceTimesVector now complete & sorted in location order
16660 
16662 /*
16663  std::ofstream LSTVFile("LSTVFile.txt");
16664  for(TLocServiceTimesVector::iterator LSTVIt = LocServiceTimesVector.begin(); LSTVIt != LocServiceTimesVector.end(); LSTVIt++)
16665  {
16666  LSTVFile << LSTVIt->Location + '\n';
16667  LSTVFile << LSTVIt->ServiceAndRepeatNum + '\n';
16668  LSTVFile << "AtLocTime = " + LSTVIt->AtLocTime + '\n';
16669  LSTVFile << "ArrTime = " + LSTVIt->ArrTime + '\n';
16670  LSTVFile << "DepTime = " + LSTVIt->DepTime + '\n';
16671  if(LSTVIt->FrhMarker == "")
16672  {
16673  LSTVFile << "Not Frh\n";
16674  }
16675  else
16676  {
16677  LSTVFile << LSTVIt->FrhMarker + '\n';
16678  }
16679  LSTVFile << '\n';
16680  }
16681  LSTVFile.close();
16682 */
16683  //declare pointers for use in printouts
16684  TLocServiceTimesVector::iterator Ptr1, Ptr2;
16685 
16686  //set up the output file
16687  AnsiString TTFileName3 = TDateTime::CurrentDateTime().FormatString("dd-mm-yyyy hh.nn.ss");
16688  TTFileName3 = CurDir + "\\Formatted timetables\\Conflict Analysis " + TTFileName3 + "; " + RailwayTitle + "; " + TimetableTitle + ".csv";
16689 
16690  std::ofstream TTFile3(TTFileName3.c_str());
16691 
16692  if(TTFile3 == 0)
16693  {
16694  ShowMessage("Conflict Analysis file failed to open - can't be created");
16695  Utilities->CallLogPop(2210);
16696  return(false);
16697  }
16698  if(LocServiceTimesVector.empty())
16699  {
16700  ShowMessage("No timetabled services found");
16701  TTFile3.close();
16702  DeleteFile(TTFileName3);
16703  Utilities->CallLogPop(2211);
16704  return(false);
16705  }
16706  TTFile3 << "Timetable analysis for timetable: '" + TimetableTitle + "' in conjunction with railway: '" + RailwayTitle + "'\n";
16707  TTFile3 << "See user manual or on-screen help section 5.12 for detailed information.\n\n\n";
16708  SequenceLog += "6\n";
16709 
16710 /* print out TrainDataVectorCopy for debugging purposes
16711 std::ofstream TDVCFile("TDVCFile.txt");
16712 for(TTrainDataVector::iterator TDVCIt = TrainDataVectorCopy.begin(); TDVCIt != TrainDataVectorCopy.end(); TDVCIt++)
16713 {
16714  TDVCFile << TDVCIt->ServiceReference + '\n';
16715  TDVCFile << TDVCIt->Description + '\n';
16716  for(unsigned int x = 0; x < TDVCIt->ActionVector.size(); x++)
16717  {
16718  TActionVectorEntry AVE = TDVCIt->ActionVector.at(x);
16719  if((AVE.FormatType == TimeCmd) || (AVE.FormatType == TimeCmdHeadCode) || (AVE.FormatType == FNSNonRepeatToShuttle) || (AVE.FormatType == FSHNewService))
16720  {
16721  TDVCFile << AnsiString(AVE.EventTime.TimeString()) << ' ' << AVE.Command << ' ' << AVE.OtherHeadCode << ' ' << AVE.NonRepeatingShuttleLinkHeadCode << '\n';
16722  }
16723  else if((AVE.FormatType == TimeLoc) && (AVE.ArrivalTime != TDateTime(-1)))
16724  {
16725  TDVCFile << AnsiString(AVE.ArrivalTime.TimeString()) << " Arr " << AVE.LocationName << '\n';
16726  }
16727  else if((AVE.FormatType == TimeLoc) && (AVE.DepartureTime != TDateTime(-1)))
16728  {
16729  TDVCFile << AnsiString(AVE.DepartureTime.TimeString()) << " Dep " << AVE.LocationName << '\n';
16730  }
16731  else if(AVE.FormatType == TimeTimeLoc)
16732  {
16733  TDVCFile << AnsiString(AVE.ArrivalTime.TimeString()) << ' ' << AnsiString(AVE.DepartureTime.TimeString()) << ' ' << AVE.LocationName << '\n';
16734  }
16735  else if(AVE.FormatType == PassTime)
16736  {
16737  TDVCFile << AnsiString(AVE.EventTime.TimeString()) << ' ' << "Pass" << ' ' << AVE.LocationName << '\n';
16738  }
16739  else if(AVE.FormatType == ExitRailway)
16740  {
16741  TDVCFile << AnsiString(AVE.EventTime.TimeString()) << " Fer" << '\n';
16742  }
16743  else if(AVE.FormatType == FinRemHere)
16744  {
16745  TDVCFile << "Frh" << '\n';
16746  }
16747  }
16748  TDVCFile << '\n';
16749 }
16750 TDVCFile.close();
16751 
16752 */
16753  //arrivals
16754  if(ArrChecked)
16755  {
16756  //sort in ArrTime order for each location
16757  Ptr1 = LocServiceTimesVector.begin();
16758  Ptr2 = Ptr1 + 1;
16759  while(Ptr2 != LocServiceTimesVector.end())
16760  {
16761  while(Ptr2->Location == Ptr1->Location) //ends with Ptr2 one past same Location value as Ptr1
16762  {
16763  Ptr2++;
16764  if(Ptr2 == LocServiceTimesVector.end())
16765  {
16766  break;
16767  }
16768  }
16769  std::sort(Ptr1, Ptr2, &LocServiceTimesArrTimeSort);
16770  Ptr1 = Ptr2; //first entry with next name
16771  if(Ptr2 != LocServiceTimesVector.end())
16772  {
16773  Ptr2++;
16774  }
16775  }
16776 
16777  //routine for arrivals - number of trains arriving within the specified range with services listed at the end
16778 
16779  TTFile3 << "Arrival & pass analysis: an asterisk means that the number of same approach code arrivals and passes is equal to or greater than the number of platforms.\n";
16780  TTFile3 << "If the total number of arrivals and passes at the same time exceeds the number of platforms the 'Trains present at location analysis' will show an asterisk.\n\n";
16781  MinuteString = " minutes";
16782  AnsiString ServiceAndRepeatNumTotal = "", ServiceAndRepeatNumTotalOutput = "";
16783  if(ArrRange == 1)
16784  {
16785  MinuteString = " minute";
16786  }
16787  TTFile3 << "Location,Number of,Number of,Services arriving within " << AnsiString(ArrRange) << MinuteString << " with their arrival times and approach codes\n";
16788  TTFile3 << ",Platforms,Trains\n\n";
16789 
16790  Ptr1 = LocServiceTimesVector.begin();
16791  Ptr2 = Ptr1 + 1;
16792  while(Ptr2 != LocServiceTimesVector.end())
16793  {
16794  PreviousService = "";
16795  NumTrainsAtLoc = 0;
16796  ServiceAndRepeatNumTotal = "";
16797  NumPlats = 0;
16798  NumPlatsAtThisLocCalculated = false;
16799  BasicTime = "";
16800  while((Ptr2->Location != Ptr1->Location) || ((Ptr1->Location == "") && (Ptr2->Location == "")))
16801  {
16802  PreviousService = "";
16803  NumTrainsAtLoc = 0;
16804  ServiceAndRepeatNumTotal = "";
16805  NumPlats = 0;
16806  NumPlatsAtThisLocCalculated = false;
16807  BasicTime = "";
16808  Ptr1++;
16809  Ptr2++;
16810  if(Ptr2 == LocServiceTimesVector.end())
16811  {
16812  break;
16813  }
16814  }
16815  if(Ptr2 == LocServiceTimesVector.end())
16816  {
16817  break;
16818  }
16819  while(Ptr2->Location == Ptr1->Location)
16820  {
16821  PreviousService = "";
16822  NumTrainsAtLoc = 0;
16823  ServiceAndRepeatNumTotal = "";
16824  BasicTime = Ptr1->ArrTime; //used to compare later times - later pointer contents have same or later times as sorted in time order
16825  if((Ptr1->Location == "") && (Ptr2->Location == ""))
16826  {
16827  break;
16828  }
16829  while(!WithinTimeRange(0, BasicTime, Ptr2->ArrTime, ArrRange) || ((Ptr1->ArrTime == "") && (Ptr2->ArrTime == "")))
16830  {
16831  BasicTime = Ptr2->ArrTime; //used to compare later times or last can exceed first
16832  Ptr1++;
16833  Ptr2++;
16834  if(Ptr2 == LocServiceTimesVector.end())
16835  {
16836  break;
16837  }
16838  if(Ptr2->Location != Ptr1->Location)
16839  {
16840  break;
16841  }
16842  }
16843  if(Ptr2 == LocServiceTimesVector.end())
16844  {
16845  break;
16846  }
16847  if(Ptr2->Location != Ptr1->Location)
16848  {
16849  break;
16850  }
16851  while(WithinTimeRange(1, BasicTime, Ptr2->ArrTime, ArrRange))
16852  {
16853  if((Ptr1->ArrTime == "") && (Ptr2->ArrTime == ""))
16854  {
16855  break;
16856  }
16857  if(!NumPlatsAtThisLocCalculated) //num plats at relevant location, reset when locations change
16858  {
16859  NumPlats = Track->NumberOfPlatforms(0, Ptr1->Location);
16860  NumPlatsAtThisLocCalculated = true;
16861  }
16862  if(Ptr1->ServiceAndRepeatNum != PreviousService) //don't print it twice if same as last - as will be if >1 service at same loc at same time
16863  {
16864  if(ServiceAndRepeatNumTotal == "")
16865  {
16866  ServiceAndRepeatNumTotal = Ptr1->ServiceAndRepeatNum + "," + Ptr1->ArrTime;
16867  NumTrainsAtLoc = 1;
16868  }
16869  else
16870  {
16871  ServiceAndRepeatNumTotal = ServiceAndRepeatNumTotal + "," + Ptr1->ServiceAndRepeatNum + "," + Ptr1->ArrTime;
16872  }
16873  }
16874  PreviousService = Ptr2->ServiceAndRepeatNum; //last service at relevant time, reset when times differ
16875  if(ServiceAndRepeatNumTotal == "")
16876  {
16877  ServiceAndRepeatNumTotal = Ptr2->ServiceAndRepeatNum + "," + Ptr2->ArrTime;
16878  NumTrainsAtLoc = 1;
16879  }
16880  else
16881  {
16882  ServiceAndRepeatNumTotal = ServiceAndRepeatNumTotal + "," + Ptr2->ServiceAndRepeatNum + "," + Ptr2->ArrTime;
16883  }
16884  Ptr1 = Ptr2;
16885  Ptr2++;
16886  if((Ptr2 == LocServiceTimesVector.end()) || (Ptr2->Location != Ptr1->Location) || (!WithinTimeRange(2, BasicTime, Ptr2->ArrTime, ArrRange)))
16887  {
16888  int MaxNumberOfSameDirections = 0;
16889  ServiceAndRepeatNumTotalOutput = ConsolidateSARNTArrDep(1, ServiceAndRepeatNumTotal, NumTrainsAtLoc, Ptr1->Location, true, AnalysisError, MaxNumberOfSameDirections); //sort into alphabetical order and remove duplicates
16890  if(AnalysisError) //has to be Ptr1->Location as Ptr2 loc may have changed
16891  {
16892 // ShowMessage("Error in arrival analysis - file will be incomplete and/or corrupt. Please send railway and timetable files to railwayfeedback@gmail.com for investigation - thanks. Details: " + ServiceAndRepeatNumTotalOutput);
16893  TTFile3.close();
16894  throw Exception(ServiceAndRepeatNumTotalOutput.c_str());
16895 // Utilities->CallLogPop(2224);
16896 // return false;
16897  }
16898  AnsiString Asterisk = "";
16899  if(MaxNumberOfSameDirections >= NumPlats)
16900  {
16901  Asterisk = "* ";
16902  }
16903  //print out a single line for number of trains at loc with all service refs
16904  TTFile3 << Asterisk << Ptr1->Location << "," << NumPlats << "," << NumTrainsAtLoc << "," << ServiceAndRepeatNumTotalOutput << '\n'; //no description as >1 service
16905  ArrivalsPrinted = true;
16906  ServiceAndRepeatNumTotal = "";
16907  }
16908  if(Ptr2 == LocServiceTimesVector.end())
16909  {
16910  break;
16911  }
16912  if(Ptr2->Location != Ptr1->Location)
16913  {
16914  break;
16915  }
16916  }
16917  if(Ptr2 == LocServiceTimesVector.end())
16918  {
16919  break;
16920  }
16921  }
16922  }
16923  if(!ArrivalsPrinted)
16924  {
16925  TTFile3 << "Nothing to report for arrivals";
16926  }
16927  TTFile3 << "\n\n";
16928  }
16929  //end of routine for arrivals
16930  SequenceLog += "7\n";
16931  //departures
16932  if(DepChecked)
16933  {
16934  //sort in DepTime order for each location
16935  Ptr1 = LocServiceTimesVector.begin();
16936  Ptr2 = Ptr1 + 1;
16937  while(Ptr2 != LocServiceTimesVector.end())
16938  {
16939  while(Ptr2->Location == Ptr1->Location) //ends with Ptr2 one past same Location value as Ptr1
16940  {
16941  Ptr2++;
16942  if(Ptr2 == LocServiceTimesVector.end())
16943  {
16944  break;
16945  }
16946  }
16947  std::sort(Ptr1, Ptr2, &LocServiceTimesDepTimeSort);
16948  Ptr1 = Ptr2; //first entry with next name
16949  if(Ptr2 != LocServiceTimesVector.end())
16950  {
16951  Ptr2++;
16952  }
16953  }
16954 
16955  //routine for departures - number of trains departing within the specified range with services listed at the end
16956  TTFile3 << "Departure & pass analysis: an asterisk means that the number of same exit code departures and passes is equal to or greater than the number of platforms.\n";
16957  TTFile3 << "If the total number of departures and passes at the same time exceeds the number of platforms the 'Trains present at location analysis' will show an asterisk.\n\n";
16958  MinuteString = " minutes";
16959  AnsiString ServiceAndRepeatNumTotal = "", ServiceAndRepeatNumTotalOutput = "";
16960  if(DepRange == 1)
16961  {
16962  MinuteString = " minute";
16963  }
16964  TTFile3 << "Location,Number of,Number of,Services departing within " << AnsiString(DepRange) << MinuteString << " with their departure times and exit codes\n";
16965  TTFile3 << ",Platforms,Trains\n\n";
16966 
16967  Ptr1 = LocServiceTimesVector.begin();
16968  Ptr2 = Ptr1 + 1;
16969  while(Ptr2 != LocServiceTimesVector.end())
16970  {
16971  PreviousService = "";
16972  NumTrainsAtLoc = 0;
16973  ServiceAndRepeatNumTotal = "";
16974  NumPlats = 0;
16975  NumPlatsAtThisLocCalculated = false;
16976  BasicTime = "";
16977  while((Ptr2->Location != Ptr1->Location) || ((Ptr1->Location == "") && (Ptr2->Location == "")))
16978  {
16979  PreviousService = "";
16980  NumTrainsAtLoc = 0;
16981  ServiceAndRepeatNumTotal = "";
16982  NumPlats = 0;
16983  NumPlatsAtThisLocCalculated = false;
16984  BasicTime = "";
16985  Ptr1++;
16986  Ptr2++;
16987  if(Ptr2 == LocServiceTimesVector.end())
16988  {
16989  break;
16990  }
16991  }
16992  if(Ptr2 == LocServiceTimesVector.end())
16993  {
16994  break;
16995  }
16996  while(Ptr2->Location == Ptr1->Location)
16997  {
16998  PreviousService = "";
16999  NumTrainsAtLoc = 0;
17000  ServiceAndRepeatNumTotal = "";
17001  BasicTime = Ptr1->DepTime; //used to compare later times - later pointer contents have same or later times as sorted in time order
17002  if((Ptr1->Location == "") && (Ptr2->Location == ""))
17003  {
17004  break;
17005  }
17006  while(!WithinTimeRange(3, BasicTime, Ptr2->DepTime, DepRange) || ((Ptr1->DepTime == "") && (Ptr2->DepTime == "")))
17007  {
17008  BasicTime = Ptr2->DepTime; //used to compare later times or last can exceed first
17009  Ptr1++;
17010  Ptr2++;
17011  if(Ptr2 == LocServiceTimesVector.end())
17012  {
17013  break;
17014  }
17015  if(Ptr2->Location != Ptr1->Location)
17016  {
17017  break;
17018  }
17019  }
17020  if(Ptr2 == LocServiceTimesVector.end())
17021  {
17022  break;
17023  }
17024  if(Ptr2->Location != Ptr1->Location)
17025  {
17026  break;
17027  }
17028  while(WithinTimeRange(4, BasicTime, Ptr2->DepTime, DepRange))
17029  {
17030  if((Ptr1->DepTime == "") && (Ptr2->DepTime == ""))
17031  {
17032  break;
17033  }
17034  if(!NumPlatsAtThisLocCalculated) //num plats at relevant location, reset when locations change
17035  {
17036  NumPlats = Track->NumberOfPlatforms(1, Ptr1->Location);
17037  NumPlatsAtThisLocCalculated = true;
17038  }
17039  if(Ptr1->ServiceAndRepeatNum != PreviousService) //don't print it twice if same as last - as will be if >1 service at same loc at same time
17040  {
17041  if(ServiceAndRepeatNumTotal == "")
17042  {
17043  ServiceAndRepeatNumTotal = Ptr1->ServiceAndRepeatNum + "," + Ptr1->DepTime;
17044  NumTrainsAtLoc = 1;
17045  }
17046  else
17047  {
17048  ServiceAndRepeatNumTotal = ServiceAndRepeatNumTotal + "," + Ptr1->ServiceAndRepeatNum + "," + Ptr1->DepTime;
17049  }
17050  }
17051  PreviousService = Ptr2->ServiceAndRepeatNum; //last service at relevant time, reset when times differ
17052  if(ServiceAndRepeatNumTotal == "")
17053  {
17054  ServiceAndRepeatNumTotal = Ptr2->ServiceAndRepeatNum + "," + Ptr2->DepTime;
17055  NumTrainsAtLoc = 1;
17056  }
17057  else
17058  {
17059  ServiceAndRepeatNumTotal = ServiceAndRepeatNumTotal + "," + Ptr2->ServiceAndRepeatNum + "," + Ptr2->DepTime;
17060  }
17061  Ptr1 = Ptr2;
17062  Ptr2++;
17063  if((Ptr2 == LocServiceTimesVector.end()) || (Ptr2->Location != Ptr1->Location) || (!WithinTimeRange(5, BasicTime, Ptr2->DepTime, DepRange)))
17064  {
17065  int MaxNumberOfSameDirections = 0;
17066  ServiceAndRepeatNumTotalOutput = ConsolidateSARNTArrDep(3, ServiceAndRepeatNumTotal, NumTrainsAtLoc, Ptr1->Location, false, AnalysisError, MaxNumberOfSameDirections); //sort into alphabetical order and remove duplicates
17067  if(AnalysisError) //has to be Ptr1->Location as Ptr2 loc may have changed
17068  {
17069 // ShowMessage("Error in departure analysis - file will be incomplete and/or corrupt. Please send railway and timetable files to railwayfeedback@gmail.com for investigation - thanks. Details: " + ServiceAndRepeatNumTotalOutput);
17070  TTFile3.close();
17071  throw Exception(ServiceAndRepeatNumTotalOutput.c_str());
17072 // Utilities->CallLogPop(2225);
17073 // return false;
17074  }
17075  AnsiString Asterisk = "";
17076  if(MaxNumberOfSameDirections >= NumPlats)
17077  {
17078  Asterisk = "* ";
17079  }
17080  //print out a single line for number of trains at loc with all service refs
17081  TTFile3 << Asterisk << Ptr1->Location << "," << NumPlats << "," << NumTrainsAtLoc << "," << ServiceAndRepeatNumTotalOutput << '\n'; //no description as >1 service
17082  DeparturesPrinted = true;
17083  ServiceAndRepeatNumTotal = "";
17084  }
17085  if(Ptr2 == LocServiceTimesVector.end())
17086  {
17087  break;
17088  }
17089  if(Ptr2->Location != Ptr1->Location)
17090  {
17091  break;
17092  }
17093  }
17094  if(Ptr2 == LocServiceTimesVector.end())
17095  {
17096  break;
17097  }
17098  }
17099  }
17100  if(!DeparturesPrinted)
17101  {
17102  TTFile3 << "Nothing to report for departures";
17103  }
17104  TTFile3 << "\n\n";
17105  }
17106  //end of routine for departures
17107  SequenceLog += "8\n";
17108 
17109  //list trains at locations at same time
17110 
17111  if(AtLocChecked)
17112  {
17113  //sort in AtLocTime order for each location
17114  Ptr1 = LocServiceTimesVector.begin();
17115  Ptr2 = Ptr1 + 1;
17116  while(Ptr2 != LocServiceTimesVector.end())
17117  {
17118  while(Ptr2->Location == Ptr1->Location) //ends with Ptr2 one past same Location value as Ptr1
17119  {
17120  Ptr2++;
17121  if(Ptr2 == LocServiceTimesVector.end())
17122  {
17123  break;
17124  }
17125  }
17126  std::sort(Ptr1, Ptr2, &LocServiceTimesAtLocTimeSort);
17127  Ptr1 = Ptr2; //first entry with next name
17128  if(Ptr2 != LocServiceTimesVector.end())
17129  {
17130  Ptr2++;
17131  }
17132  }
17133 
17134  //print out simultaneous AtLocs (don't need range of times for AtLocs)
17135  TTFile3 << "Trains present at location analysis: an asterisk means that the number of trains at the location is greater than the number of platforms.\n\n";
17136  TTFile3 << "Location,Number of,Number of,Time,Services at the location at that time\n";
17137  TTFile3 << ",Platforms,Trains,\n\n";
17138  AnsiString ServiceAndRepeatNumTotal = "", ServiceAndRepeatNumTotalOutput = "";
17139  Ptr1 = LocServiceTimesVector.begin();
17140  Ptr2 = Ptr1 + 1;
17141  while(Ptr2 != LocServiceTimesVector.end())
17142  {
17143  PreviousService = "";
17144  ServiceAndRepeatNumTotal = "";
17145  NumTrainsAtLoc = 0;
17146  NumPlats = 0;
17147  NumPlatsAtThisLocCalculated = false;
17148  FrhCount = 0;
17149  while((Ptr2->Location != Ptr1->Location) || ((Ptr1->Location == "") && (Ptr2->Location == "")))
17150  {
17151  PreviousService = "";
17152  ServiceAndRepeatNumTotal = "";
17153  NumTrainsAtLoc = 0;
17154  NumPlats = 0;
17155  NumPlatsAtThisLocCalculated = false;
17156  FrhCount = 0;
17157  Ptr1++;
17158  Ptr2++;
17159  if(Ptr2 == LocServiceTimesVector.end())
17160  {
17161  break;
17162  }
17163  }
17164  if(Ptr2 == LocServiceTimesVector.end())
17165  {
17166  break;
17167  }
17168  while(Ptr2->Location == Ptr1->Location)
17169  {
17170  if(Ptr1->FrhMarker == "Frh") //this test is made here and each time Ptr1 increases with Ptr1 & 2 at same loc so as to catch them all
17171  {
17172  FrhCount++;
17173  Ptr1->FrhMarker = "FrhCounted"; //to avoid double counting
17174  }
17175  PreviousService = "";
17176  NumTrainsAtLoc = 0;
17177  ServiceAndRepeatNumTotal = "";
17178  if((Ptr1->Location == "") && (Ptr2->Location == ""))
17179  {
17180  break;
17181  }
17182  while((Ptr2->AtLocTime != Ptr1->AtLocTime) || ((Ptr1->AtLocTime == "") && (Ptr2->AtLocTime == "")))
17183  {
17184  Ptr1++;
17185  if(Ptr1->FrhMarker == "Frh")
17186  {
17187  FrhCount++;
17188  Ptr1->FrhMarker = "FrhCounted"; //to avoid double counting
17189  }
17190  Ptr2++;
17191  if(Ptr2 == LocServiceTimesVector.end())
17192  {
17193  break;
17194  }
17195  if(Ptr2->Location != Ptr1->Location)
17196  {
17197  break;
17198  }
17199  }
17200  if(Ptr2 == LocServiceTimesVector.end())
17201  {
17202  break;
17203  }
17204  if(Ptr2->Location != Ptr1->Location)
17205  {
17206  break;
17207  }
17208  while(Ptr2->AtLocTime == Ptr1->AtLocTime)
17209  {
17210  if((Ptr1->AtLocTime == "") && (Ptr2->AtLocTime == ""))
17211  {
17212  break;
17213  }
17214  if(!NumPlatsAtThisLocCalculated) //num plats at relevant location, reset when locations change
17215  {
17216  NumPlats = Track->NumberOfPlatforms(2, Ptr1->Location);
17217  NumPlatsAtThisLocCalculated = true;
17218  }
17219  if(Ptr1->ServiceAndRepeatNum != PreviousService) //don't print it twice if same as last - as will be if >1 service at same loc at same time
17220  {
17221  if(ServiceAndRepeatNumTotal == "")
17222  {
17223  ServiceAndRepeatNumTotal = Ptr1->ServiceAndRepeatNum;
17224  NumTrainsAtLoc = 1;
17225  }
17226  else
17227  {
17228  ServiceAndRepeatNumTotal = ServiceAndRepeatNumTotal + "," + Ptr1->ServiceAndRepeatNum;
17229  }
17230  }
17231  PreviousService = Ptr2->ServiceAndRepeatNum; //last service at relevant time, reset when times differ, has to be Ptr2 to compare Ptr1 at next round when incremented
17232  if(ServiceAndRepeatNumTotal == "")
17233  {
17234  ServiceAndRepeatNumTotal = Ptr2->ServiceAndRepeatNum;
17235  NumTrainsAtLoc = 1;
17236  }
17237  else
17238  {
17239  ServiceAndRepeatNumTotal = ServiceAndRepeatNumTotal + "," + Ptr2->ServiceAndRepeatNum;
17240  }
17241  Ptr1 = Ptr2;
17242  if(Ptr1->FrhMarker == "Frh")
17243  {
17244  FrhCount++;
17245  Ptr1->FrhMarker = "FrhCounted"; //to avoid double counting
17246  }
17247  Ptr2++;
17248  if((Ptr2 == LocServiceTimesVector.end()) || (Ptr2->Location != Ptr1->Location) || (Ptr2->AtLocTime != Ptr1->AtLocTime))
17249  {
17250 //old text //only print out if no remainers (1st condition), change in remainers (2nd condition) or change in ServiceAndRepeatNumTotalOutput, and >1 train (later condition)
17251 //new text //don't print out if all remainers or if only 1 train at loc
17252  ServiceAndRepeatNumTotalOutput = ConsolidateSARNTAtLoc(1, ServiceAndRepeatNumTotal, NumTrainsAtLoc); //sort into alphabetical order, remove duplicates, and calculate new value for NumTrainsAtLoc
17253 //old condits if((FrhCount == 0) || (FrhCount != LastFrhCount) || (PreviousServiceAndRepeatNumTotalOutput != ServiceAndRepeatNumTotalOutput))//don't print if same output
17254 /*new condits*/ if((NumTrainsAtLoc > 1) && ((FrhCount < NumTrainsAtLoc) || (FrhCount != LastFrhCount)))
17255  {
17256  AnsiString Asterisk = "";
17257  if(NumTrainsAtLoc > NumPlats)
17258  {
17259  Asterisk = "* ";
17260  }
17261  //print out a single line for number of trains at loc with all service refs
17262  if(FrhCount == 0)
17263  {
17264  TTFile3 << Asterisk << Ptr1->Location << "," << NumPlats << "," << NumTrainsAtLoc << "," << Ptr1->AtLocTime << "," << ServiceAndRepeatNumTotalOutput << '\n';
17265  }
17266  else if(FrhCount == 1)
17267  {
17268  TTFile3 << Asterisk << Ptr1->Location << "," << NumPlats << "," << NumTrainsAtLoc << "," << Ptr1->AtLocTime << " (1 remains here)," << ServiceAndRepeatNumTotalOutput << '\n';
17269  }
17270  else
17271  {
17272  TTFile3 << Asterisk << Ptr1->Location << "," << NumPlats << "," << NumTrainsAtLoc << "," << Ptr1->AtLocTime << " (" << FrhCount << " remain here)," << ServiceAndRepeatNumTotalOutput << '\n';
17273  }
17274  LastFrhCount = FrhCount;
17275  PreviousServiceAndRepeatNumTotalOutput = ServiceAndRepeatNumTotalOutput;
17276  AtLocsPrinted = true;
17277  ServiceAndRepeatNumTotal = "";
17278  }
17279  }
17280  if(Ptr2 == LocServiceTimesVector.end())
17281  {
17282  break;
17283  }
17284  if(Ptr2->Location != Ptr1->Location)
17285  {
17286  break;
17287  }
17288  }
17289  if(Ptr2 == LocServiceTimesVector.end())
17290  {
17291  break;
17292  }
17293  }
17294  }
17295  if(!AtLocsPrinted)
17296  {
17297  TTFile3 << "Nothing to report for trains at locations";
17298  }
17299  TTFile3 << "\n\n";
17300  //end of simultaneous AtLocs
17301  }
17302  SequenceLog += "9\n";
17303 /*
17304  //print out the full vector here for testing purposes
17305  TTFile3 << "Full LocServiceTimesVector\n\n";
17306  TTFile3 << "Location,AtLocTime,ArrTime,DepTime,ServiceAndRepeatNum,Description\n\n";
17307 
17308  for(TLocServiceTimesVector::iterator Ptr = LocServiceTimesVector.begin(); Ptr != LocServiceTimesVector.end(); Ptr++)
17309  {
17310  TTFile3 << Ptr->Location << "," << Ptr->AtLocTime << "," << Ptr->ArrTime << "," << Ptr->DepTime << "," << Ptr->ServiceAndRepeatNum << "," << Ptr->FrhMarker << '\n';
17311  }
17312 
17313  TTFile3 << "\n\n\n";
17314 */
17315 /*cdt analysis - added at v2.10.0
17316 2 pass system: 1st extract as a single service all Snt (or Snt-sh) starts, with Fns/Sns links combined (and F-nshs/Sns-sh) (though add a new
17317 changeover code [chr XXXX - 'change ref + new reference] until come to Fjo, Frh, Frh-sh, Fer (ignore exit loc as can't stop there), ignore jbos &
17318 repeats, but with fsp & rsp store all the foregoing service entries along with the split reference & add that to the relevant Sfs entry as a new
17319 service. For shuttles with feeder start with feeder & progress into shuttle, ending when finish & remain here or progressing into the finishing
17320 service.
17321 
17322 Use The TrainDataVectorCopy as that has all unique service refs.
17323 
17324 2nd run the cdt checker similar to that in SecondPassActions, but where a same name found either side of a changeover code quote both refs. Add a
17325 similar unexpected cdt check where if have different locs either side of a cdt then may be inappropriate.
17326 
17327 First create a new TrainDataVector from earlier copy as above with single services
17328 */
17329  if(DirChecked)
17330  {
17331  //direction analysis added at v2.10.0
17332  TTrainDataEntry SingleServiceEntry, PartServiceEntry, NewPartServiceEntry, TempEntry;
17333  TTrainDataVector SingleServiceVector, PartServiceVector;
17334 
17335  //find new train services (Snt or Snt-sh) & remember that entries can be in any order
17336  //NB: ALWAYS use OtherHeadCode (which is now a service reference) for any follow-on service
17337  TTFile3 << "Train direction analysis - consisting of train facing directions on creation and possible missing or unexpected changes of direction:\n\n";
17338  for(unsigned int x = 0; x < TrainDataVectorCopy.size(); x++)
17339  {
17340  TTrainDataEntry TDE = TrainDataVectorCopy.at(x);
17341  if(TDE.ActionVector.at(TDE.ActionVector.size() - 1).FormatType == Repeat)
17342  {
17343  TDE.ActionVector.erase(&TDE.ActionVector.back()); //strip repeat entry if present
17344  }
17345  const TActionVector &AV = TDE.ActionVector;
17346  if((AV.at(0).Command == "Snt") || (AV.at(0).Command == "Snt-sh"))
17347  {
17348  SingleServiceEntry = TDE;
17349  TActionVector &SSAV = SingleServiceEntry.ActionVector;
17350  for(unsigned int y = 0; y < SSAV.size(); y++)
17351  {
17352  if((SSAV.at(y).Command == "Fjo") || (SSAV.at(y).Command == "Frh") || (SSAV.at(y).Command == "Fer") || (SSAV.at(y).Command == "Frh-sh"))
17353  {
17354  SingleServiceVector.push_back(SingleServiceEntry); //push the complete entry
17355  break; //finished with this one
17356  }
17357  else if((SSAV.at(y).Command == "fsp") || (SSAV.at(y).Command == "rsp"))
17358  {
17359  PartServiceEntry = TDE; //start with complete entry
17360  PartServiceEntry.ActionVector.clear(); //clear AV
17361  for(unsigned int z = 0; z <= y; z++)
17362  {
17363  PartServiceEntry.ActionVector.push_back(TDE.ActionVector.at(z)); //add back all AVs up to & inc fsp/rsp
17364  if(z == y)
17365  {
17366  PartServiceEntry.ActionVector.at(z).Command = "chr-sp"; //change split command to chr
17367  PartServiceEntry.ActionVector.at(z).OtherHeadCode = PartServiceEntry.ActionVector.at(z).LinkedTrainEntryPtr->ServiceReference;
17368  }
17369  }
17370  PartServiceVector.push_back(PartServiceEntry);
17371  if(SSAV.at(y).Command == "fsp")
17372  {
17373  SSAV.at(y).Command = "Front split - original service continues below";
17374  SSAV.at(y).OtherHeadCode = "";
17375  }
17376  if(SSAV.at(y).Command == "rsp")
17377  {
17378  SSAV.at(y).Command = "Rear split - original service continues below";
17379  SSAV.at(y).OtherHeadCode = "";
17380  }
17381  //don't break & continue here because the original train carries on
17382  }
17383  else if(SSAV.at(y).Command == "Fns")
17384  {
17385  SSAV.at(y).Command = "chr-Fns";
17386  SSAV.at(y).OtherHeadCode = SSAV.at(y).LinkedTrainEntryPtr->ServiceReference;
17387  PartServiceVector.push_back(SingleServiceEntry); //not complete yet
17388  break; //from y loop
17389  }
17390  else if(SSAV.at(y).Command == "Fns-sh")
17391  {
17392  SSAV.at(y).Command = "chr-Fns-sh";
17393  SSAV.at(y).OtherHeadCode = SSAV.at(y).NonRepeatingShuttleLinkEntryPtr->ServiceReference;
17394  SSAV.at(y).NonRepeatingShuttleLinkHeadCode = "";
17395  PartServiceVector.push_back(SingleServiceEntry); //not complete yet
17396  break; //from y loop
17397  }
17398  else if(SSAV.at(y).Command == "F-nshs")
17399  {
17400  SSAV.at(y).Command = "chr-F-nshs"; //NonRepeatingShuttleLinkHeadCode is the shuttle headcode
17401  SSAV.at(y).OtherHeadCode = SSAV.at(y).LinkedTrainEntryPtr->ServiceReference;
17402  SSAV.at(y).NonRepeatingShuttleLinkHeadCode = "";
17403  PartServiceVector.push_back(SingleServiceEntry); //not complete yet
17404  break; //from y loop
17405  }
17406  }
17407  }
17408  }
17409  SequenceLog += "10\n";
17410  //now have all complete entries in SingleServiceVector and all part services in PartServiceVector but without any follow-ons
17411 
17412  //Now add Sns & Sns-sh services to PartServiceVector entries
17413  AnsiString NextRef;
17414  while(!PartServiceVector.empty())
17415  {
17416  PartServiceEntry = PartServiceVector.at(0); //deal with front entry and add new entries at the back
17417  for(unsigned int y = 0; y < PartServiceEntry.ActionVector.size(); y++)
17418  {
17419  if(PartServiceEntry.ActionVector.at(y).Command.SubString(1,3) == "chr")
17420  {
17421  NextRef = PartServiceEntry.ActionVector.at(y).OtherHeadCode;
17422  }
17423  }
17424  //find it in TrainDataVectorCopy
17425  bool FinishType = true, FoundFlag = false;
17426  while(FinishType)
17427  {
17428  TempEntry = GetServiceFromVector(0, NextRef, TrainDataVectorCopy, FinishType, FoundFlag); //FinishType is a bool where false = Final (Fjo, Frh, Fer, or
17429  //Frh-sh); true = MoreToCome (Fns, Fns-sh, F-nshs)
17430  if(FoundFlag)
17431  {
17432  for(unsigned int y = 1; y < TempEntry.ActionVector.size(); y++) //starts at 1 as that is the entry after the start entry
17433  {
17434  if((TempEntry.ActionVector.at(y).Command == "") && (TempEntry.ActionVector.at(y).FormatType != Repeat))
17435  {
17436  PartServiceVector.at(0).ActionVector.push_back(TempEntry.ActionVector.at(y));
17437  }
17438  else if((TempEntry.ActionVector.at(y).Command[1] != 'F') && (TempEntry.ActionVector.at(y).Command != "fsp") && (TempEntry.ActionVector.at(y).Command != "rsp") && (TempEntry.ActionVector.at(y).FormatType != Repeat))
17439  {
17440  PartServiceVector.at(0).ActionVector.push_back(TempEntry.ActionVector.at(y));
17441  }
17442  else
17443  {
17444  if((TempEntry.ActionVector.at(y).Command == "Fjo") || (TempEntry.ActionVector.at(y).Command == "Frh") || (TempEntry.ActionVector.at(y).Command == "Fer") || (TempEntry.ActionVector.at(y).Command == "Frh-sh"))
17445  {
17446  PartServiceVector.at(0).ActionVector.push_back(TempEntry.ActionVector.at(y));
17447  SingleServiceVector.push_back(PartServiceVector.at(0)); //push the complete entry
17448  PartServiceVector.erase(&PartServiceVector.at(0));
17449  break; //from y loop
17450  }
17451  else if((TempEntry.ActionVector.at(y).Command == "fsp") || (TempEntry.ActionVector.at(y).Command == "rsp"))
17452  {
17453  NewPartServiceEntry = PartServiceVector.at(0); //covers everything up to but excluding the split
17454  NewPartServiceEntry.ActionVector.push_back(TempEntry.ActionVector.at(y)); //now includes the split
17455  NewPartServiceEntry.ActionVector.at(NewPartServiceEntry.ActionVector.size() - 1).Command = "chr-sp"; //change split command to chr
17456  NewPartServiceEntry.ActionVector.at(NewPartServiceEntry.ActionVector.size() - 1).OtherHeadCode = NewPartServiceEntry.ActionVector.at(NewPartServiceEntry.ActionVector.size() - 1).LinkedTrainEntryPtr->ServiceReference;
17457  PartServiceVector.push_back(NewPartServiceEntry); //new entry for the split service
17458  if(TempEntry.ActionVector.at(y).Command == "fsp")
17459  {
17460  TempEntry.ActionVector.at(y).Command = "Front split - original service continues below";
17461  TempEntry.ActionVector.at(y).OtherHeadCode = "";
17462  }
17463  if(TempEntry.ActionVector.at(y).Command == "rsp")
17464  {
17465  TempEntry.ActionVector.at(y).Command = "Rear split - original service continues below";
17466  TempEntry.ActionVector.at(y).OtherHeadCode = "";
17467  }
17468  PartServiceVector.at(0).ActionVector.push_back(TempEntry.ActionVector.at(y));
17469  }
17470  else if(TempEntry.ActionVector.at(y).Command == "Fns")
17471  {
17472  TempEntry.ActionVector.at(y).Command = "chr-Fns";
17473  NextRef = TempEntry.ActionVector.at(y).LinkedTrainEntryPtr->ServiceReference;
17474  TempEntry.ActionVector.at(y).OtherHeadCode = NextRef;
17475  PartServiceVector.at(0).ActionVector.push_back(TempEntry.ActionVector.at(y)); //not complete yet
17476  break; //from y loop
17477  }
17478  else if(TempEntry.ActionVector.at(y).Command == "Fns-sh")
17479  {
17480  TempEntry.ActionVector.at(y).Command = "chr-Fns-sh";
17481  TempEntry.ActionVector.at(y).OtherHeadCode = TempEntry.ActionVector.at(y).NonRepeatingShuttleLinkEntryPtr->ServiceReference;
17482  TempEntry.ActionVector.at(y).NonRepeatingShuttleLinkHeadCode = "";
17483  NextRef = TempEntry.ActionVector.at(y).NonRepeatingShuttleLinkEntryPtr->ServiceReference;
17484  PartServiceVector.at(0).ActionVector.push_back(TempEntry.ActionVector.at(y)); //not complete yet
17485  break; //from y loop
17486  }
17487  else if(TempEntry.ActionVector.at(y).Command == "F-nshs")
17488  {
17489  TempEntry.ActionVector.at(y).Command = "chr-F-nshs"; //NonRepeatingShuttleLinkHeadCode is the shuttle headcode
17490  TempEntry.ActionVector.at(y).OtherHeadCode = TempEntry.ActionVector.at(y).LinkedTrainEntryPtr->ServiceReference;
17491  TempEntry.ActionVector.at(y).NonRepeatingShuttleLinkHeadCode = "";
17492  NextRef = TempEntry.ActionVector.at(y).LinkedTrainEntryPtr->ServiceReference;
17493  PartServiceVector.at(0).ActionVector.push_back(TempEntry.ActionVector.at(y)); //not complete yet
17494  break; //from y loop
17495  }
17496  }
17497  }
17498  }
17499  else
17500  {
17501  SequenceLog += + "11\n";
17502  throw Exception("Unable to find service reference " + NextRef + " Last ref checked = " + TempEntry.ServiceReference);
17503  }
17504  }
17505  }
17506  if(!PartServiceVector.empty())
17507  {
17508  SequenceLog += "12\n";
17509  throw Exception("PartServiceVector should be empty here - size = " + PartServiceVector.size());
17510  }
17511  SequenceLog += "13\n";
17512  /*
17513  form:-
17514  HeadCode[;Description (plain text, no commas or semicolons)][;StartSpeed(kph); MaxRunningSpeed(kph); Mass(tonnes, prog converts to kg);
17515  MaxBrakeRate(tonnes force, prog converts to m/s/s); & gross power(kW, prog converts to power at rail in w)
17516  then multiple entries, separated by commas, of the form:-
17517 
17518  HH:MM;Snt;RearStartIdent FrontStartIdent }StartNew }
17519  HH:MM;Snt-sh;RearStartIdent FrontStartIdent;Fsh HeadCode }SNTShuttle }
17520  HH:MM;Sns-sh;Fxx-sh HeadCode;F-nshs HeadCode (non-repeating)}SNSShuttle }
17521 
17522  HH:MM;Command;HeadCode (Sfs Sns jbo fsp rsp Fns Fjo Frh-sh) }TimeCmdHeadCode } Train action entries
17523  HH:MM;F-nshs;NonRepeatingShuttleLinkHeadCode }FNSNonRepeatToShuttle }
17524  HH:MM;Sns-fsh;NonRepeatingShuttleLinkHeadCode }SNSNonRepeatFromShuttle }
17525 
17526  HH:MM;Command (cdt) }TimeCmd }
17527  HH:MM;Location (arr & dep) }TimeLoc }
17528  HH:MM;HH:MM;Location }TimeTimeLoc }
17529  HH:MM;pas;Location }PassTime }
17530  HH:MM;Fns-sh;Snx-sh HeadCode;Sns-fsh HeadCode (non-rep) }FSHNewService }
17531  HH:MM;Fer;set of allowable IDs }ExitRailway }
17532  Command (Frh only) }FinRemHere }
17533 
17534  R;mm;dd;nn. Repeat Repeat entry
17535 
17536  Formats:
17537 
17538  Command only: Frh
17539  Time;Command: cdt
17540  Time;Command;Headcode: Sfs Sns jbo fsp rsp Fns Fjo Frh-sh F-nshs Sns-fsh
17541  Time;Command;2 Element IDs: Snt
17542  Time;Comand;n Element IDs: Fer
17543  Time;Command;rep Headcode;nonrep Headcode: Sns-sh Fns-sh
17544  Time;Command;2 Element IDs;Headcode Snt-sh
17545  Time;Command;Location pas
17546  Time;Location Arr Dep
17547  Time;Time;Location Arr & dep together
17548  */
17549 
17550 /*
17551 Perform the starting direction check (Snt & Snt-sh entries). Starts with the train's front element,
17552 checking forwards until it comes to a continuation (no report), a location name that is not null and
17553 different to the train's front element name (whether null or not) (no report), a leading point
17554 (no report) or buffers (report).
17555 */
17556  bool BufferFacingUnReportedFlag = true;
17557  for(unsigned int x = 0; x < SingleServiceVector.size(); x++)
17558  {
17559  TTrackElement ThisElement, NextElement;
17560  TTrainDataEntry TDE = SingleServiceVector.at(x);
17561  if(TDE.ActionVector.at(TDE.ActionVector.size() - 1).FormatType == Repeat)
17562  {
17563  SequenceLog += "13a\n";
17564  throw Exception("Repeat entry present in SingleServiceVector at position " + x);
17565  }
17566  const TActionVector &AV = TDE.ActionVector;
17567  if((AV.at(0).Command == "Snt") || (AV.at(0).Command == "Snt-sh"))
17568  {
17569  bool BufferFlag = false;
17570  int FrontTVPos = AV.at(0).FrontStartOrRepeatDigits;
17571  int RearTVPos = AV.at(0).RearStartOrRepeatMins;
17572  AnsiString FrontLocName = AV.at(0).LocationName;
17573  int NextEntryPos, NextExitPos;
17574  ThisElement = Track->TrackElementAt(1395, FrontTVPos);
17575  int ThisExitPos;
17576  if(ThisElement.Conn[0] == RearTVPos)
17577  {
17578  ThisExitPos = 1;
17579  }
17580  else if(ThisElement.Conn[1] == RearTVPos)
17581  {
17582  ThisExitPos = 0;
17583  }
17584  else if(ThisElement.Conn[2] == RearTVPos)
17585  {
17586  ThisExitPos = 3;
17587  }
17588  else if(ThisElement.Conn[3] == RearTVPos)
17589  {
17590  ThisExitPos = 2;
17591  }
17592  if((ThisElement.TrackType == Buffers) && (ThisExitPos == 0))//pos 0 is the buffer
17593  {
17594  BufferFlag = true;
17595  }
17596  else //continue tracking forwards
17597  {
17598  while(true)
17599  {
17600  if(ThisElement.Conn[ThisExitPos] == -1)
17601  {
17602  SequenceLog = "13b\n";
17603  throw Exception("ThisElement connects to -1 for " + TDE.ServiceReference);
17604  }
17605  NextElement = Track->TrackElementAt(1396, ThisElement.Conn[ThisExitPos]);
17606  NextEntryPos = ThisElement.ConnLinkPos[ThisExitPos];
17607  if((NextElement.TrackType == Points) && ((NextEntryPos == 0) || (NextEntryPos == 2))) //leading points
17608  {
17609  BufferFlag = false; //should already be false
17610  break;
17611  }
17612  else if(NextElement.TrackType == Continuation)
17613  {
17614  BufferFlag = false;
17615  break;
17616  }
17617  else if((NextElement.ActiveTrackElementName != "") && (NextElement.ActiveTrackElementName != FrontLocName))
17618  {
17619  BufferFlag = false;
17620  break;
17621  }
17622  else if(NextElement.TrackType == Buffers)
17623  {
17624  BufferFlag = true;
17625  break;
17626  }
17627  else if((NextElement.TrackType == Points) && ((NextEntryPos == 1) || (NextEntryPos == 3))) //trailing points
17628  {
17629  ThisElement = NextElement;
17630  ThisExitPos = 0;
17631  continue;
17632  }
17633  else
17634  {
17635  if(NextEntryPos == 0)
17636  {
17637  NextExitPos = 1;
17638  }
17639  else if(NextEntryPos == 1)
17640  {
17641  NextExitPos = 0;
17642  }
17643  else if(NextEntryPos == 2)
17644  {
17645  NextExitPos = 3;
17646  }
17647  else if(NextEntryPos == 3)
17648  {
17649  NextExitPos = 2;
17650  }
17651  }
17652  ThisElement = NextElement;
17653  ThisExitPos = NextExitPos;
17654  }
17655  }
17656  if(BufferFlag)
17657  {
17658  if(BufferFacingUnReportedFlag)
17659  {
17660  TTFile3 << "Train facing direction on creation analysis:-\n\n";
17661  BufferFacingUnReportedFlag = false;
17662  }
17663  TTFile3 << "Service " + TDE.ServiceReference + " facing buffers on creation\n";
17664  }
17665  }
17666  }
17667  if(BufferFacingUnReportedFlag)
17668  {
17669  TTFile3 << "Nothing to report for train facing directions\n\n";
17670  }
17671  else
17672  {
17673  TTFile3 << '\n';
17674  }
17675  SequenceLog += "13c\n";
17676 
17677  //Perform the missing cdt check. Check every entry simiar to the check in SecondPassActions and if find any print out the full sequence and service entries
17678  AnsiString LocationNameToBeChecked = "";
17679  bool MissingcdtUnreportedFlag = true;
17680  TNumList MarkerList;
17681  for(unsigned int x = 0; x < SingleServiceVector.size(); x++)
17682  {
17683  const TTrainDataEntry &TDEntry = SingleServiceVector.at(x);
17684  unsigned int y = 0;
17685  int FirstInstance = 9999, SecondInstance = 9999; //9999 ensures wont be marked if not changed
17686  bool FullBreak = false;
17687  MarkerList.clear();
17688  const TActionVectorEntry &AVEntry0 = TDEntry.ActionVector.at(0);
17689  // first discard unlocated Snt entries as they don't have location name set
17690  if((AVEntry0.Command == "Snt") && (AVEntry0.LocationType == EnRoute))
17691  {
17692  y = 1;
17693  }
17694  while((y < TDEntry.ActionVector.size()) && !FullBreak)
17695  // need to check each location name separately in turn, skipped for SignallerControl entries
17696  {
17697  if((TDEntry.ActionVector.at(y).Command == "Fer") || (TDEntry.ActionVector.at(y).FormatType == Repeat) ||
17698  (TDEntry.ActionVector.at(y).Command == "Fjo") || (TDEntry.ActionVector.at(y).Command == "Frh") ||
17699  (TDEntry.ActionVector.at(y).Command == "Frh-sh"))
17700  {
17701  break; // out of the 'while' loop since have reached the end
17702  }
17703  LocationNameToBeChecked = TDEntry.ActionVector.at(y).LocationName;
17704  FirstInstance = y;
17705  for(unsigned int z = y; z < TDEntry.ActionVector.size(); z++)
17706  {
17707  const TActionVectorEntry &AVEntry = TDEntry.ActionVector.at(z);
17708  if((AVEntry.Command == "Fer") || (AVEntry.FormatType == Repeat) ||
17709  (AVEntry.Command == "Fjo") || (AVEntry.Command == "Frh") ||
17710  (AVEntry.Command == "Frh-sh"))
17711  {
17712  break; // out of the 'z' loop since have reached the end & 'Fer' & 'Repeat' have no location name set
17713  }
17714  if(AVEntry.Command == "cdt")
17715  {
17716  break; // out of the 'z' loop since the check is only valid up to a change of direction
17717  }
17718  if(AVEntry.LocationName == LocationNameToBeChecked)
17719  {
17720  continue; // keep going while name same
17721  }
17722  if(AVEntry.LocationName != LocationNameToBeChecked)
17723  // if name different check forwards to see if repeats
17724  {
17725  for(unsigned int a = z; a < TDEntry.ActionVector.size(); a++)
17726  {
17727  if(TDEntry.ActionVector.at(a).Command == "cdt")
17728  {
17729  break; // out of the 'a' & 'z' loops since the check is only valid up to a change of direction
17730  }
17731  if(TDEntry.ActionVector.at(a).LocationName == LocationNameToBeChecked)
17732  {
17733  SecondInstance = a;
17734  AnsiString Sequence = TDEntry.ServiceReference;
17735  for(unsigned int b = 0; b < TDEntry.ActionVector.size(); b++)
17736  {
17737  if(TDEntry.ActionVector.at(b).Command.SubString(1,3) == "chr")
17738  {
17739  Sequence = Sequence + AnsiString(" -> ") + TDEntry.ActionVector.at(b).OtherHeadCode;
17740  }
17741  }
17742  if(MissingcdtUnreportedFlag)
17743  {
17744  TTFile3 << "Possibly missing changes of direction - these will be missing unless the service travels in a loop back to the locations marked:-\n\n";
17745  }
17746  TTFile3 << LocationNameToBeChecked << " is listed twice with no direction change between in service sequence: " << Sequence << "\n\n";
17747  MarkerList.push_back(FirstInstance);
17748  MarkerList.push_back(SecondInstance);
17749  SingleServiceOutput(0, x, MarkerList, SingleServiceVector, TTFile3);
17750  MissingcdtUnreportedFlag = false;
17751  FullBreak = true; //no more checks for this sequence
17752  break; //out of the a & z loops
17753  }
17754  }
17755  break; // out of the 'z' loop since have checked 'a' as far as need to
17756  }
17757  }
17758  y++;
17759  }
17760  }
17761  if(MissingcdtUnreportedFlag)
17762  {
17763  TTFile3 << "Nothing to report for missing changes of direction\n\n";
17764  }
17765  else
17766  {
17767  TTFile3 << '\n';
17768  }
17769  SequenceLog += "14\n";
17770 
17771 /* Perform the unexpected cdt check. Examine each service in the SingleServiceVector, and if don't find the same
17772  name either side of a cdt (before the next cdt) then flag as an unexpected cdt.
17773  Method: Have an outer loop for each service that looks for cdts. When found work backwards to the last cdt or beginning and std::list all the
17774  locations excluding the cdt location. Then work forwards to the next cdt or the end and do the same. Sort each list and make unique (duplicated
17775  names on one side of a cdt already checked either by the tt validator or the missing cdt check. Then compare the two lists and if any location
17776  included in both then ok, else report as unexpected. If one list is empty then it is reported.
17777 */
17778  typedef std::list<AnsiString> TLocList;
17779  TLocList BackwardList, ForwardList;
17780  bool IntroLineNeeded = true;
17781  for(unsigned int x = 0; x < SingleServiceVector.size(); x++)
17782  {
17783  unsigned int cdtPosition = 9999;
17784  AnsiString cdtLocation = "";
17785  bool FoundSameName = false;
17786  MarkerList.clear();
17787  const TTrainDataEntry &TDEntry = SingleServiceVector.at(x);
17788  for(unsigned int y = 0; y < TDEntry.ActionVector.size(); y++)
17789  // need to check each location name separately in turn, skipped for SignallerControl entries
17790  {
17791  BackwardList.clear();
17792  ForwardList.clear();
17793  const TActionVectorEntry &AVEntry = TDEntry.ActionVector.at(y);
17794  if((AVEntry.Command == "Fer") || (AVEntry.FormatType == Repeat) || //shouldn't be any repeats
17795  (AVEntry.Command == "Fjo") || (AVEntry.Command == "Frh") ||
17796  (AVEntry.Command == "Frh-sh"))
17797  {
17798  if(MarkerList.empty())
17799  {
17800  break; // out of the 'y' loop since have reached the end & nothing to report
17801  }
17802  else
17803  {
17804  AnsiString Sequence = TDEntry.ServiceReference;
17805  for(unsigned int b = 0; b < TDEntry.ActionVector.size(); b++)
17806  {
17807  if(TDEntry.ActionVector.at(b).Command.SubString(1,3) == "chr")
17808  {
17809  Sequence = Sequence + AnsiString(" -> ") + TDEntry.ActionVector.at(b).OtherHeadCode;
17810  }
17811  }
17812  MarkerList.sort();
17813  if(IntroLineNeeded)
17814  {
17815  TTFile3 << "Unexpected change of direction analysis.\n\n";
17816  TTFile3 << "For marked changes of direction there are no same-name locations listed both above and below.\n";
17817  TTFile3 << "These changes of direction are probably valid for movements to and from depots but all should be checked to\n";
17818  TTFile3 << "make sure that none has been included incorrectly:\n\n";
17819  IntroLineNeeded = false;
17820  }
17821  TTFile3 << "Service sequence " << Sequence << " contains unexpected changes of direction:-\n\n";
17822  SingleServiceOutput(1, x, MarkerList, SingleServiceVector, TTFile3);
17823  break;
17824  }
17825  }
17826  if(AVEntry.Command != "cdt")
17827  {
17828  continue; //only looking for cdts
17829  }
17830  //here have found a cdt
17831  cdtPosition = y;
17832  cdtLocation = AVEntry.LocationName;
17833  for(int z = y - 1; z >= 0; z--)
17834  {
17835  const TActionVectorEntry &AVEntry2 = TDEntry.ActionVector.at(z);
17836  if(AVEntry2.Command == "cdt")
17837  {
17838  break; //don't look further back than the last cdt
17839  }
17840  if((AVEntry2.LocationName != "") && (AVEntry2.LocationName != cdtLocation)) //if an earlier entry == cdtLocation will have been picked up in an earlier check
17841  {
17842  BackwardList.push_back(AVEntry2.LocationName);
17843  }
17844  }
17845  BackwardList.sort();
17846  BackwardList.unique();
17847  for(unsigned int z = y + 1; z < TDEntry.ActionVector.size(); z++)
17848  {
17849  const TActionVectorEntry &AVEntry3 = TDEntry.ActionVector.at(z);
17850  if((AVEntry3.Command == "Fer") || (AVEntry3.FormatType == Repeat) ||
17851  (AVEntry3.Command == "Fjo") || (AVEntry3.Command == "Frh") ||
17852  (AVEntry3.Command == "Frh-sh") || (AVEntry3.Command == "cdt"))
17853  {
17854  break; // out of the 'z' loop since have reached another cdt or the end
17855  }
17856  if((AVEntry3.LocationName != "") && (AVEntry3.LocationName != cdtLocation)) //if a later entry == cdtLocation will have been picked up in an earlier check
17857  {
17858  ForwardList.push_back(AVEntry3.LocationName);
17859  }
17860  }
17861  ForwardList.sort();
17862  ForwardList.unique();
17863  FoundSameName = false;
17864  //now have both lists compiled (may be empty) so check for same name in both & report if don't find any
17865  if(!BackwardList.empty() && !ForwardList.empty())
17866  {
17867  for(TLocList::iterator BLIt = BackwardList.begin(); BLIt != BackwardList.end(); BLIt++)
17868  {
17869  for(TLocList::iterator FLIt = ForwardList.begin(); FLIt != ForwardList.end(); FLIt++)
17870  {
17871  if(*BLIt == *FLIt)
17872  {
17873  FoundSameName = true;
17874  }
17875  }
17876  }
17877  }
17878  if(!FoundSameName) //report the inability to find same name
17879  {
17880  MarkerList.push_back(cdtPosition);
17881  }
17882  }
17883  }
17884  if(IntroLineNeeded)
17885  {
17886  TTFile3 << "Nothing to report for unexpected changes of direction\n\n";
17887  }
17888  else
17889  {
17890  TTFile3 << '\n';
17891  }
17892 /*
17893 //print all SSVector for diagnostic purposes
17894  TTFile3 << "Whole SSVector\n\n";
17895  TNumList EmptyList;
17896  for(unsigned int x = 0; x < SingleServiceVector.size(); x++)
17897  {
17898  SingleServiceOutput(, x, EmptyList, SingleServiceVector, TTFile3);
17899  }
17900 */
17901  }
17902  SequenceLog += "15\n";
17903  TTFile3.close();
17904  Utilities->CallLogPop(2212);
17905  return(true);
17906  }
17907 
17908  catch(const Exception &e) //non error catch
17909  {
17910  AnsiString TTErrorFileName = "Analysis Error.txt";
17911  TTErrorFileName = CurDir + "\\Formatted timetables\\" + TTErrorFileName;
17912  std::ofstream TTError(TTErrorFileName.c_str());
17913  if(TTError == 0)
17914  {
17915  ShowMessage("Analysis error file failed to open - can't be created");
17916  Utilities->CallLogPop(2233);
17917  return(false);
17918  }
17919  AnsiString TimeNow = TDateTime::CurrentDateTime().FormatString("dd-mm-yyyy hh.nn.ss");
17920  TTError << TimeNow.c_str() << '\n' << ArrRange << '\n' << ArrChecked << '\n' << DepRange << '\n' <<
17921  DepChecked << '\n' << AtLocChecked << '\n' << SequenceLog << '\n' << AnsiString(e.Message);
17922 
17923  TTError.close();
17924  ShowMessage("Error in Conflict Analysis: A file called 'Analysis Error.txt' has been created in your Formatted timetables folder. Please send this file together with your railway and timetable files to railwayfeedback@gmail.com for investigation - many thanks");
17925  Utilities->CallLogPop(2226);
17926  return(false);
17927  }
17928 }
17929 
17930 // ---------------------------------------------------------------------------
17931 void TTrainController::SingleServiceOutput(int Caller, int SSVectorNumber, TNumList MarkerList, TTrainDataVector &SingleServiceVector, std::ofstream &VecFile)
17932 {
17933  Utilities->CallLog.push_back(Utilities->TimeStamp() + AnsiString(SSVectorNumber) + ',' + ",SingleServiceOutput");
17934  if((SSVectorNumber < 0) || ((unsigned int)SSVectorNumber >= SingleServiceVector.size()))
17935  {
17936  throw Exception("SSVectorNumber out of range, = " + AnsiString(SSVectorNumber) + ", Vector size = " + SingleServiceVector.size());
17937  }
17938  TTrainDataEntry SingleService = SingleServiceVector.at(SSVectorNumber);
17939  {
17940  VecFile << ",Initial service reference " << SingleService.ServiceReference + '\n';
17941  AnsiString Marker = "";
17942  for(unsigned int x = 0; x < SingleService.ActionVector.size(); x++)
17943  {
17944  Marker = ',';
17945  for(TNumListIterator MLIt = MarkerList.begin(); MLIt != MarkerList.end(); MLIt++)
17946  {
17947  if(int(x) == *MLIt)
17948  {
17949  Marker = "-->,";
17950  break;
17951  }
17952  }
17953  TActionVectorEntry AVE = SingleService.ActionVector.at(x);
17954  if(AVE.FormatType == StartNew)
17955  {
17956  AnsiString RearID = Track->TrackElementAt(1397, AVE.RearStartOrRepeatMins).ElementID;
17957  AnsiString FrontID = Track->TrackElementAt(1398, AVE.FrontStartOrRepeatDigits).ElementID;
17958  VecFile << Marker << AnsiString(AVE.EventTime.TimeString()) << ' ' << AVE.Command << ' ' << RearID << ' ' << FrontID << '\n';
17959  }
17960  if(AVE.FormatType == SNTShuttle)
17961  {
17962  AnsiString RearID = Track->TrackElementAt(1399, AVE.RearStartOrRepeatMins).ElementID;
17963  AnsiString FrontID = Track->TrackElementAt(1400, AVE.FrontStartOrRepeatDigits).ElementID;
17964  VecFile << Marker << AnsiString(AVE.EventTime.TimeString()) << ' ' << AVE.Command << ' ' << RearID << ' ' << FrontID << ' ' << AVE.OtherHeadCode << '\n';
17965  }
17966  if(AVE.FormatType == SNSShuttle) //should all have been converted to chr
17967  {
17968  VecFile << Marker << AnsiString(AVE.EventTime.TimeString()) << ' ' << AVE.Command << ' ' << AVE.OtherHeadCode << ' ' << AVE.NonRepeatingShuttleLinkHeadCode << '\n';
17969  }
17970  if(AVE.FormatType == Repeat) //shouldn't be any repeats, only here to show if any have been copied
17971  {
17972  VecFile << Marker << "Repeat " << AVE.RearStartOrRepeatMins << ' ' << AVE.FrontStartOrRepeatDigits << ' ' << AVE.NumberOfRepeats << '\n';
17973  }
17974  if((AVE.FormatType == TimeCmd) || (AVE.FormatType == TimeCmdHeadCode) || (AVE.FormatType == FNSNonRepeatToShuttle) || (AVE.FormatType == FSHNewService))
17975  {
17976  TActionVectorEntry AVHolder = AVE;
17977  if(AVE.Command.SubString(1,3) == "chr")
17978  {
17979  if(AVE.Command.SubString(5, AVE.Command.Length() - 4) == "sp")
17980  {
17981  AVE.Command = "Change of service to " + AVE.OtherHeadCode + " after split";
17982  AVE.OtherHeadCode = "";
17983  }
17984  if(AVE.Command.SubString(5, AVE.Command.Length() - 4) == "Fns")
17985  {
17986  AVE.Command = "Change of service to ";
17987  }
17988  if(AVE.Command.SubString(5, AVE.Command.Length() - 4) == "Fns-sh")
17989  {
17990  AVE.Command = "Change to shuttle finishing service";
17991  }
17992  if(AVE.Command.SubString(5, AVE.Command.Length() - 4) == "F-nshs")
17993  {
17994  AVE.Command = "Change to shuttle service " + AVE.OtherHeadCode + " from feeder";
17995  AVE.OtherHeadCode = "";
17996  }
17997  }
17998  VecFile << Marker << AnsiString(AVE.EventTime.TimeString()) << ' ' << AVE.Command << ' ' << AVE.OtherHeadCode << '\n';
17999  AVE = AVHolder;
18000  }
18001  else if((AVE.FormatType == TimeLoc) && (AVE.ArrivalTime != TDateTime(-1)))
18002  {
18003  VecFile << Marker << AnsiString(AVE.ArrivalTime.TimeString()) << " Arr " << AVE.LocationName << '\n';
18004  }
18005  else if((AVE.FormatType == TimeLoc) && (AVE.DepartureTime != TDateTime(-1)))
18006  {
18007  VecFile << Marker << AnsiString(AVE.DepartureTime.TimeString()) << " Dep " << AVE.LocationName << '\n';
18008  }
18009  else if(AVE.FormatType == TimeTimeLoc)
18010  {
18011  VecFile << Marker << AnsiString(AVE.ArrivalTime.TimeString()) << ' ' << AnsiString(AVE.DepartureTime.TimeString()) << ' ' << AVE.LocationName << '\n';
18012  }
18013  else if(AVE.FormatType == PassTime)
18014  {
18015  VecFile << Marker << AnsiString(AVE.EventTime.TimeString()) << ' ' << "Pass" << ' ' << AVE.LocationName << '\n';
18016  }
18017  else if(AVE.FormatType == ExitRailway)
18018  {
18019  AnsiString ListOfExits = "";
18020  for(TNumListIterator NLIt = AVE.ExitList.begin(); NLIt != AVE.ExitList.end(); NLIt++)
18021  {
18022  ListOfExits += AnsiString(Track->TrackElementAt(1406, *NLIt).ElementID) + ' ';
18023  }
18024  VecFile << Marker << AnsiString(AVE.EventTime.TimeString()) << " Fer " << ListOfExits <<'\n';
18025  }
18026  else if(AVE.FormatType == FinRemHere)
18027  {
18028  VecFile << Marker << "Frh" << '\n';
18029  }
18030  }
18031  VecFile << '\n';
18032  }
18033  Utilities->CallLogPop(2318);
18034 }
18035 
18036 // ---------------------------------------------------------------------------
18037 
18038 TTrainDataEntry TTrainController::GetServiceFromVector(AnsiString Caller, AnsiString ServiceReference, TTrainDataVector Vector, bool &FinishType, bool &FoundFlag)
18039 {
18040  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",GetServiceFromVector," + ServiceReference);
18041  FoundFlag = false;
18042  FinishType = true;
18043  for(unsigned int x = 0; x < Vector.size(); x++)
18044  {
18045  if(Vector.at(x).ServiceReference == ServiceReference)
18046  {
18047  if(Vector.at(x).ActionVector.at(Vector.at(x).ActionVector.size() - 1).FormatType == Repeat) //shouldn't be any repeats
18048  {
18049  TActionVectorEntry AVE = Vector.at(x).ActionVector.at(Vector.at(x).ActionVector.size() - 2);
18050  if((AVE.Command == "Fjo") || (AVE.Command == "Frh") || (AVE.Command == "Fer") || (AVE.Command == "Frh-sh"))
18051  {
18052  FinishType = false;
18053  }
18054  }
18055  else
18056  {
18057  TActionVectorEntry AVE = Vector.at(x).ActionVector.at(Vector.at(x).ActionVector.size() - 1);
18058  if((AVE.Command == "Fjo") || (AVE.Command == "Frh") || (AVE.Command == "Fer") || (AVE.Command == "Frh-sh"))
18059  {
18060  FinishType = false;
18061  }
18062  }
18063  FoundFlag = true;
18064  Utilities->CallLogPop(2319);
18065  return(Vector.at(x));
18066  }
18067  }
18068  Utilities->CallLogPop(2320);
18069  return(Vector.at(Vector.size() - 1)); //return last for want of returning something
18070 }
18071 
18072 // ---------------------------------------------------------------------------
18073 
18074 bool TTrainController::WithinTimeRange(int Caller, AnsiString Time1, AnsiString Time2, int MinuteRange) //times are "HH:MM"
18075 {
18076 //convert times to integer minutes
18077  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",WithinTimeRange," + Time1 + "," + Time2 + "," + AnsiString(MinuteRange));
18078  if((Time1 == "") || (Time2 == ""))
18079  {
18080  Utilities->CallLogPop(2213);
18081  return(false);
18082  }
18083  int Mins = Time1.SubString(4,2).ToInt();
18084  int Hours = Time1.SubString(1,2).ToInt();
18085  int Time1Mins = (Hours * 60) + Mins;
18086  Mins = Time2.SubString(4,2).ToInt();
18087  Hours = Time2.SubString(1,2).ToInt();
18088  int Time2Mins = (Hours * 60) + Mins;
18089  if(abs(Time1Mins - Time2Mins) <= MinuteRange)
18090  {
18091  Utilities->CallLogPop(2214);
18092  return(true);
18093  }
18094  Utilities->CallLogPop(2215);
18095  return(false);
18096 }
18097 
18098 // ---------------------------------------------------------------------------
18099 
18100 AnsiString TTrainController::ConsolidateSARNTArrDep(int Caller, const AnsiString Input, int &NumTrainsAtLoc, AnsiString Location, bool Arrival,
18101  bool &AnalysisError, int &MaxNumberOfSameDirections)
18102 {
18103  //input consists of services and service Arr or Dep times as a comma separated list, Location needed to determine direction information
18104 
18105  try
18106  {
18107  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",ConsolidateSARNTArrDep," + Input);
18108  AnsiString Output = "", OneService = "", TempStr1 = "", TempStr2 = "";
18109  int SCPos = 0;
18110  std::list<AnsiString> ServiceList; //this is the list of services with times extracted from Input - not to be confused with ServiceCallingPointsList
18111  //first change every second comma in Input to a semicolon so can separate services but keep times with services
18112  bool EvenComma = false;
18113  for(int x = 1; x <= Input.Length(); x++)
18114  {
18115  TempStr1 = Input[x];
18116  if(TempStr1 == AnsiString(',') && EvenComma)
18117  {
18118  TempStr2 += ';';
18119  }
18120  else
18121  {
18122  TempStr2 += Input[x];
18123  }
18124  if(TempStr1 == AnsiString(','))
18125  {
18126  EvenComma = !EvenComma;
18127  }
18128  }
18129  //load up the list of services with associated times
18130  while(TempStr2.Length() > 0)
18131  {
18132  SCPos = TempStr2.Pos(';');
18133  if(SCPos > 0) //0 if not found, as won't be when only one service left
18134  {
18135  OneService = TempStr2.SubString(1, SCPos - 1);
18136  ServiceList.push_back(OneService);
18137  TempStr2 = TempStr2.SubString(SCPos + 1, TempStr2.Length() - SCPos);
18138  }
18139  else //no semicolon so looking at last (or only) element
18140  {
18141  ServiceList.push_back(TempStr2);
18142  TempStr2 = "";
18143  }
18144  }
18145  ServiceList.sort(); // alphabetical order
18146  ServiceList.unique(); //remove duplicates
18147  NumTrainsAtLoc = ServiceList.size(); //calc this after removing duplicates as may not have changed
18148 
18149  //now add direction information from AllServiceCallingLocsMap - key is service ref and value a list of calling points in order
18150  int DirectionMarker = 0; //this is added in & runs from 1 upwards, same marker for diff services = same direction
18151  //first add the direction marker "&0" for not yet allocated - '&' is an identifier
18152  std::list<AnsiString>::iterator SLIt, SLIt1, SLIt2, SLIt3;
18153 
18154  for(SLIt = ServiceList.begin(); SLIt != ServiceList.end(); SLIt++)
18155  {
18156  *SLIt = *SLIt + "&0"; //add in a basic direction marker to each service
18157  }
18158  SLIt3 = ServiceList.end();
18159  SLIt3--; //so points to last element
18160  AnsiString ServiceRef1, ServiceRef2, AnsiTime1, AnsiTime2, RepeatInfo1, RepeatInfo2; //1 refers to first for..next loop & 2 to second
18161  int AmpersandPos, SpacePos, CommaPos1, CommaPos2, RepeatNum1, RepeatNum2;
18162  TAllServiceCallingLocsMap::iterator ASCLIt1, ASCLIt2;
18163  TServiceCallingLocsList ServiceCallingLocsList1, ServiceCallingLocsList2;
18164  MaxNumberOfSameDirections = 0; //at end of each SLIt loop if SameDirectionCount > MaxNumberOfSameDirections then MaxNumberOfSameDirections = SameDirectionCount
18165  int SameDirectionCount = 0; //starts at 1 at each SLIt loop (because SLIt1 entry already has a DirectionMarker) and increments for every same direction
18166 
18167  for(std::list<AnsiString>::iterator SLIt1 = ServiceList.begin(); SLIt1 != SLIt3; SLIt1++) //should be end() - 1 but can't use -1 with lists so have to improvise
18168  {
18169  SLIt = SLIt1;
18170  SLIt++; //so points to one after SLIt1
18171  if(SLIt1->SubString(SLIt1->Length() - 1, 2) != AnsiString("&0"))
18172  {
18173  continue; //already allocated so skip to the next
18174  }
18175  else
18176  {
18177  CommaPos1 = SLIt1->Pos(','); //can't be 0
18178  ServiceRef1 = SLIt1->SubString(1, CommaPos1 - 1);
18179  //but this contains "(First service..." etc so need to strip these, but use to extract RepeatNum
18180  SpacePos = ServiceRef1.Pos(' ');
18181  RepeatNum1 = 0;
18182  if(SpacePos > 0) //otherwise it's already correct
18183  {
18184  RepeatInfo1 = ServiceRef1.SubString(SpacePos + 2, ServiceRef1.Length() - SpacePos - 2); //drops the brackets and leaves "First service", "Repeat 2" etc
18185  ServiceRef1 = ServiceRef1.SubString(1, SpacePos - 1);
18186  if(RepeatInfo1[1] == 'F')
18187  {
18188  RepeatNum1 = 0;
18189  }
18190  else
18191  {
18192  SpacePos = RepeatInfo1.Pos(' ');
18193  RepeatNum1 = RepeatInfo1.SubString(SpacePos + 1, RepeatInfo1.Length() - SpacePos).ToInt();
18194  }
18195  }
18196  AnsiTime1 = SLIt1->SubString(CommaPos1 + 1, SLIt1->Length() - CommaPos1);
18197  //but this includes the "&0" etc so need to strip these
18198  AmpersandPos = AnsiTime1.Pos('&');
18199  AnsiTime1 = AnsiTime1.SubString(1, AmpersandPos - 1);
18200 
18201  ASCLIt1 = AllServiceCallingLocsMap.find(ServiceRef1);
18202  if(ASCLIt1 == AllServiceCallingLocsMap.end()) //can't find it
18203  {
18204  throw Exception("ASCLIt1 Error in " + Input);
18205  }
18206  ServiceCallingLocsList1 = ASCLIt1->second;
18207  AmpersandPos = SLIt1->Pos('&');
18208  *SLIt1 = SLIt1->SubString(1, AmpersandPos); //truncate up to & leave in the '&'
18209  *SLIt1 = *SLIt1 + AnsiString(++DirectionMarker); //now add the next marker (pre-increment), allow for it being more than one digit
18210 
18211  SameDirectionCount = 1;
18212  for(SLIt2 = SLIt; SLIt2 != ServiceList.end(); SLIt2++)
18213  {
18214  CommaPos2 = SLIt2->Pos(','); //can't be 0
18215  ServiceRef2 = SLIt2->SubString(1, CommaPos2 - 1);
18216  //but this contains "(First service..." etc so need to strip these
18217  SpacePos = ServiceRef2.Pos(' ');
18218  RepeatNum2 = 0;
18219  if(SpacePos > 0) //otherwise it's already correct
18220  {
18221  RepeatInfo2 = ServiceRef2.SubString(SpacePos + 2, ServiceRef2.Length() - SpacePos - 2); //drops the brackets and leaves "First service", "Repeat 2" etc
18222  ServiceRef2 = ServiceRef2.SubString(1, SpacePos - 1);
18223  if(RepeatInfo2[1] == 'F')
18224  {
18225  RepeatNum2 = 0;
18226  }
18227  else
18228  {
18229  SpacePos = RepeatInfo2.Pos(' ');
18230  RepeatNum2 = RepeatInfo2.SubString(SpacePos + 1, RepeatInfo2.Length() - SpacePos).ToInt();
18231  }
18232  }
18233  AnsiTime2 = SLIt2->SubString(CommaPos2 + 1, SLIt2->Length() - CommaPos2);
18234  //but this includes the "&0" etc so need to strip these
18235  AmpersandPos = AnsiTime2.Pos('&');
18236  AnsiTime2 = AnsiTime2.SubString(1, AmpersandPos - 1);
18237 
18238  ASCLIt2 = AllServiceCallingLocsMap.find(ServiceRef2);
18239  if(ASCLIt2 == AllServiceCallingLocsMap.end()) //can't find it
18240  {
18241  throw Exception("ASCLIt2 Error in " + Input);
18242  }
18243  ServiceCallingLocsList2 = ASCLIt2->second;
18244  //now compare the two
18245  if(SameDirection(0, ServiceRef1, ServiceRef2, AnsiTime1, AnsiTime2, RepeatNum1, RepeatNum2, ServiceCallingLocsList1, ServiceCallingLocsList2, Location, Arrival))
18246  {
18247  int AmpersandPos = SLIt2->Pos('&');
18248  *SLIt2 = SLIt2->SubString(1, AmpersandPos); //truncate up to & leave in the '&'
18249  *SLIt2 = *SLIt2 + AnsiString(DirectionMarker); //now add the same marker as *SLIt1
18250  SameDirectionCount++;
18251  }
18252  }
18253  if(SameDirectionCount > MaxNumberOfSameDirections)
18254  {
18255  MaxNumberOfSameDirections = SameDirectionCount;
18256  }
18257  }
18258  }
18259 
18260  if(SLIt3->SubString(SLIt3->Length() - 1, 2) == AnsiString("&0")) //*SLTIt3 is the last in the list and may not have been allocated, if not it doesn't match
18261  {
18262  //any existing direction so allocate it now
18263  AmpersandPos = SLIt3->Pos('&');
18264  *SLIt3 = SLIt3->SubString(1, AmpersandPos); //truncate up to & leave in the '&'
18265  *SLIt3 = *SLIt3 + AnsiString(++DirectionMarker);
18266  }
18267  //now change direction markers to upper case letters beginning with 'A' (and continuing with 'AA' if exceed 26) & add a comma before so have ServiceRef, DirectionMarker, Time
18268  for(SLIt = ServiceList.begin(); SLIt != ServiceList.end(); SLIt++)
18269  {
18270  //extract the DirectionMarker as an integer
18271  AmpersandPos = SLIt->Pos('&');
18272  AnsiString DirectionMarkerString = SLIt->SubString(AmpersandPos + 1, SLIt->Length() - AmpersandPos); //extract the number as an ansistring
18273  AnsiString ServiceWithoutMarker = SLIt->SubString(1, AmpersandPos - 1); //truncate the &number
18274  DirectionMarker = DirectionMarkerString.ToInt();
18275  AnsiString DirectionSuffix = "";
18276  char c;
18277  if(DirectionMarker < 27)
18278  {
18279  c = 64 + DirectionMarker; //so 1 -> 'A'
18280  DirectionSuffix = "," + AnsiString(c);
18281  }
18282  else if(DirectionMarker < 53)
18283  {
18284  c = 65 + DirectionMarker - 27; //so 27 -> 'AA'
18285  DirectionSuffix = ",A" + AnsiString(c);
18286  }
18287  else
18288  {
18289  DirectionSuffix = ",?"; //shouldn'tn ever get this far!
18290  }
18291  *SLIt = ServiceWithoutMarker + DirectionSuffix;
18292  }
18293  //now prepare the final consolidated output
18294  for(SLIt = ServiceList.begin(); SLIt != ServiceList.end(); SLIt++)
18295  {
18296  Output = Output + *SLIt + ","; //will end up with an unwanted comma at the end
18297  }
18298  if(Output.Length() > 0)
18299  {
18300  Output = Output.SubString(1, Output.Length() - 1); //remove the last comma
18301  }
18302  Utilities->CallLogPop(2216);
18303  return(Output);
18304  }
18305 
18306  catch(const Exception &e) //non error catch
18307  {
18308  AnalysisError = true;
18309  Utilities->CallLogPop(2227);
18310  return(e.Message);
18311  }
18312 }
18313 
18314 // ---------------------------------------------------------------------------
18315 
18316 AnsiString TTrainController::ConsolidateSARNTAtLoc(int Caller, const AnsiString Input, int &NumTrainsAtLoc)
18317 {
18318  //similar to above but doesn't include times in the input
18319  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",ConsolidateSARNTAtLoc," + Input);
18320  AnsiString InternalInput = Input, Output = "", OneService = "";
18321  int CommaPos = 0;
18322  std::list<AnsiString> ServiceList;
18323  //load up the list
18324  while(InternalInput.Length() > 0)
18325  {
18326  CommaPos = InternalInput.Pos(',');
18327  if(CommaPos > 0) //0 if not found, as won't be when only one service left
18328  {
18329  OneService = InternalInput.SubString(1, CommaPos - 1);
18330  ServiceList.push_back(OneService);
18331  InternalInput = InternalInput.SubString(CommaPos + 1, InternalInput.Length() - CommaPos);
18332  }
18333  else //no comma so looking at last (or only) element
18334  {
18335  ServiceList.push_back(InternalInput);
18336  InternalInput = "";
18337  }
18338  }
18339 
18340  ServiceList.sort(); // alphabetical order
18341  ServiceList.unique(); //remove duplicates
18342  NumTrainsAtLoc = ServiceList.size(); //calc this after removing duplicates as may not have changed
18343  for(std::list<AnsiString>::iterator SLIt = ServiceList.begin(); SLIt != ServiceList.end(); SLIt++)
18344  {
18345  Output = Output + *SLIt + ","; //will end up with an unwanted comma at the end
18346  }
18347  if(Output.Length() > 0)
18348  {
18349  Output = Output.SubString(1, Output.Length() - 1); //remove the last comma
18350  }
18351  Utilities->CallLogPop(2217);
18352  return(Output);
18353 }
18354 
18355 // ---------------------------------------------------------------------------
18356 
18357 
18358 bool TTrainController::SameDirection(int Caller, AnsiString Ref1In, AnsiString Ref2In, AnsiString Time1, AnsiString Time2, int RepeatNum1, int RepeatNum2, TServiceCallingLocsList List1,
18359  TServiceCallingLocsList List2, AnsiString Location, bool Arrival)
18360 {
18361  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SameDirection," + Ref1In + "," + Ref2In + "," + Time1 + "," + Time2 + "," +
18362  AnsiString(RepeatNum1) + "," + AnsiString(RepeatNum2) + "," + Location);
18363 
18364  std::list<AnsiString>::iterator LP1 = 0, LP2 = 0, ListPtr1 = 0, ListPtr2 = 0, LocPtr1 = 0, LocPtr2 = 0; //LP1 & 2 are temporary pointers, ListPtrs are
18365  //general list pointers, LocPtrs point to Location in the two lists
18366 
18367  //first find the relevant values for LocPtr1 & LocPtr2 taking account of cdts and times
18368  //for List1
18369  bool LocFound = false;
18370  AnsiString Ref1 = Ref1In, Ref2 = Ref2In;
18371  int IncMinutes;
18372  TDateTime FirstServiceTime;
18373 
18374  //first need to strip off /1, /2 etc if present from Ref1 & Ref2 (leave Ref1In & Ref2In for error message & retain value as target in finding the correct reference for cdts)
18375  int Ref1Target = 0, Ref1Count = 0;
18376  int SlashPos = Ref1.Pos('/');
18377  if(SlashPos > 0) //if 0 Ref1 == Ref1In & target stays at 0
18378  {
18379  Ref1Target = Ref1.SubString(SlashPos + 1, Ref1.Length() - SlashPos).ToInt();
18380  Ref1 = Ref1.SubString(1, SlashPos - 1); //truncate up to but omit '/'
18381  }
18382  int Ref2Target = 0, Ref2Count = 0;
18383  SlashPos = Ref2.Pos('/');
18384  if(SlashPos > 0) //if 0 leave as is
18385  {
18386  Ref2Target = Ref2.SubString(SlashPos + 1, Ref2.Length() - SlashPos).ToInt();
18387  Ref2 = Ref2.SubString(1, SlashPos - 1); //truncate up to but omit '/'
18388  }
18389  for(ListPtr1 = List1.begin(); ListPtr1 != List1.end(); ListPtr1++) //note that when this routine entered Ref1In & Ref2In are already set to the correct services,
18390  {
18391  //even if others have same names. But if there are cdt's then need to refind the correct service
18392  if((*ListPtr1) == Location) //
18393  {
18394  LocPtr1 = ListPtr1; //may be modified later
18395  LocFound = true;
18396  }
18397  if(ListPtr1->SubString(1, 3) == "%%%")
18398  {
18399  AnsiString CDTTime = ListPtr1->SubString(4, 5);
18400  //now adjust the time to correspond to the repeat if there is one
18401  if(RepeatNum1 > 0) //if it is 0 then AnsiTime1 is already valid
18402  {
18403  IncMinutes = -1;
18404  FirstServiceTime = TDateTime(-1);
18405  bool BreakFlag = false;
18406  for(TTrainDataVector::iterator TDVIt = TrainDataVector.begin(); TDVIt != TrainDataVector.end(); TDVIt++)
18407  {
18408  if(TDVIt->ServiceReference == Ref1)
18409  {
18410  if(Ref1Target > Ref1Count)
18411  {
18412  Ref1Count++;
18413  continue;
18414  }
18415  IncMinutes = TDVIt->ActionVector.back().RearStartOrRepeatMins;
18416  for(TActionVector::iterator AVIt = TDVIt->ActionVector.begin(); AVIt != TDVIt->ActionVector.end(); AVIt++)
18417  {
18418  if(Utilities->Format96HHMM(AVIt->EventTime) == CDTTime)
18419  {
18420  FirstServiceTime = AVIt->EventTime; //i.e. the FirstService value of CDTTime
18421  BreakFlag = true;
18422  break;
18423  }
18424  if(Utilities->Format96HHMM(AVIt->ArrivalTime) == CDTTime) //add arr & dep in case find sooner (though dep shouldn't be sooner)
18425  {
18426  FirstServiceTime = AVIt->ArrivalTime;
18427  BreakFlag = true;
18428  break;
18429  }
18430  if(Utilities->Format96HHMM(AVIt->DepartureTime) == CDTTime)
18431  {
18432  FirstServiceTime = AVIt->DepartureTime;
18433  BreakFlag = true;
18434  break;
18435  }
18436  }
18437  if(BreakFlag)
18438  {
18439  break;
18440  }
18441  }
18442  }
18443  if(IncMinutes == -1)
18444  {
18445  throw Exception("Failed to find service for ServiceRef1 in SameDirection " + Ref1In + " " + Ref2In + " " + Time1 + " " + Time2 + " " + AnsiString(RepeatNum1) + " " + AnsiString(RepeatNum2) + " " + Location);
18446  }
18447  if(FirstServiceTime == TDateTime(-1))
18448  {
18449  throw Exception("Failed to find first service time for ServiceRef1 in SameDirection " + Ref1In + " " + Ref2In + " " + Time1 + " " + Time2 + " " + AnsiString(RepeatNum1) + " " + AnsiString(RepeatNum2) + " " + Location);
18450  }
18451  CDTTime = Utilities->Format96HHMM(TrainController->GetRepeatTime(60, FirstServiceTime, RepeatNum1, IncMinutes));
18452  }
18453  if(!Arrival && (Time1 == CDTTime)) //continue if equal in case next is a departure for the Location
18454  {
18455  LocFound = false;
18456  continue;
18457  }
18458  if(Arrival && (Time1 == CDTTime)) //gone far enough so can stop
18459  {
18460  break;
18461  }
18462  if(Time1 > CDTTime) //not there yet so go on
18463  {
18464  LocFound = false;
18465  continue;
18466  }
18467  if(Time1 < CDTTime) //gone too far so can stop now
18468  {
18469  break;
18470  }
18471  }
18472  }
18473  if(!LocFound) //have to find it in both lists
18474  {
18475  Utilities->CallLogPop(2228);
18476  return( false);
18477  }
18478  //for List2
18479  LocFound = false;
18480  for(ListPtr2 = List2.begin(); ListPtr2 != List2.end(); ListPtr2++)
18481  {
18482  if((*ListPtr2) == Location)
18483  {
18484  LocPtr2 = ListPtr2; //may be modified later
18485  LocFound = true;
18486  }
18487  if(ListPtr2->SubString(1, 3) == "%%%")
18488  {
18489  AnsiString CDTTime = ListPtr2->SubString(4, 5);
18490  //now adjust the time to correspond to the repeat if there is one
18491  if(RepeatNum2 > 0) //if it is 0 then AnsiTime1 is already valid
18492  {
18493  IncMinutes = -1;
18494  FirstServiceTime = TDateTime(-1);
18495  bool BreakFlag = false;
18496  for(TTrainDataVector::iterator TDVIt = TrainDataVector.begin(); TDVIt != TrainDataVector.end(); TDVIt++)
18497  {
18498  if(TDVIt->ServiceReference == Ref2)
18499  {
18500  if(Ref2Target > Ref2Count)
18501  {
18502  Ref2Count++;
18503  continue;
18504  }
18505  IncMinutes = TDVIt->ActionVector.back().RearStartOrRepeatMins;
18506  for(TActionVector::iterator AVIt = TDVIt->ActionVector.begin(); AVIt != TDVIt->ActionVector.end(); AVIt++)
18507  {
18508  if(Utilities->Format96HHMM(AVIt->EventTime) == CDTTime)
18509  {
18510  FirstServiceTime = AVIt->EventTime;
18511  BreakFlag = true;
18512  break;
18513  }
18514  if(Utilities->Format96HHMM(AVIt->ArrivalTime) == CDTTime)
18515  {
18516  FirstServiceTime = AVIt->ArrivalTime;
18517  BreakFlag = true;
18518  break;
18519  }
18520  if(Utilities->Format96HHMM(AVIt->DepartureTime) == CDTTime)
18521  {
18522  FirstServiceTime = AVIt->DepartureTime;
18523  BreakFlag = true;
18524  break;
18525  }
18526  }
18527  if(BreakFlag)
18528  {
18529  break;
18530  }
18531  }
18532  }
18533  if(IncMinutes == -1)
18534  {
18535  throw Exception("IncMinutes -1 for ServiceRef2 in SameDirection " + Ref1In + " " + Ref2In + " " + Time1 + " " + Time2 + " " + AnsiString(RepeatNum1) + " " + AnsiString(RepeatNum2) + " " + Location);
18536  }
18537  if(FirstServiceTime == TDateTime(-1))
18538  {
18539  throw Exception("First service time -1 for ServiceRef2 in SameDirection " + Ref1In + " " + Ref2In + " " + Time1 + " " + Time2 + " " + AnsiString(RepeatNum1) + " " + AnsiString(RepeatNum2) + " " + Location);
18540  }
18541  CDTTime = Utilities->Format96HHMM(TrainController->GetRepeatTime(61, FirstServiceTime, RepeatNum2, IncMinutes));
18542  }
18543  if(!Arrival && (Time2 == CDTTime)) //continue if equal in case next is a departure for the Location
18544  {
18545  LocFound = false;
18546  continue;
18547  }
18548  if(Arrival && (Time2 == CDTTime)) //gone far enough so can stop
18549  {
18550  break;
18551  }
18552  if(Time2 > CDTTime) //not there yet so go on
18553  {
18554  LocFound = false;
18555  continue;
18556  }
18557  if(Time2 < CDTTime) //gone too far so can stop now
18558  {
18559  break;
18560  }
18561  }
18562  }
18563  if(!LocFound) //have to find it in both lists, and should be found but allow for it not being
18564  {
18565  Utilities->CallLogPop(2229);
18566  return( false);
18567  }
18568  //now, for the arrival analysis, see if there is a common location before the LocPtrs & within any cdts, and if so return true, else return false
18569  //set ListPtr1 to the search start position
18570  if(Arrival)
18571  {
18572  LP1 = List1.begin();
18573  LP1--; //now points to before the first entry
18574  for(ListPtr1 = LocPtr1; ListPtr1 != LP1; ListPtr1--) //search backwards from Location
18575  {
18576  if(ListPtr1 == List1.begin())
18577  {
18578  break;
18579  }
18580  if(ListPtr1->SubString(1, 3) == "%%%") //a cdt event
18581  {
18582  ListPtr1++; //point to one past the cdt
18583  break;
18584  }
18585  }
18586  //set ListPtr2 to the search start position
18587  LP2 = List2.begin();
18588  LP2--; //now points to before the first entry
18589  for(ListPtr2 = LocPtr2; ListPtr2 != LP2; ListPtr2--)
18590  {
18591  if(ListPtr2 == List2.begin())
18592  {
18593  break;
18594  }
18595  if(ListPtr2->SubString(1, 3) == "%%%") //a cdt event
18596  {
18597  ListPtr2++; //point to one past the cdt
18598  break;
18599  }
18600  }
18601  //ListPtr1 & 2 now at search start position
18602  LP1 = ListPtr1;
18603  LP2 = ListPtr2;
18604  //now search forwards, i.e. for common locations before Location
18605  for(ListPtr1 = LP1; ListPtr1 != List1.end(); ListPtr1++)
18606  {
18607  if(ListPtr1 == LocPtr1) //reached Location without finding a common earlier location so skip to the backwards check
18608  {
18609  break;
18610  }
18611  if(ListPtr1->SubString(1, 3) == "%%%") //reached a cdt event without finding a common earlier location
18612  {
18613  break;
18614  }
18615  for(ListPtr2 = LP2; ListPtr2 != List2.end(); ListPtr2++)
18616  {
18617  if(ListPtr2 == LocPtr2) //not found common earlier location so go to the next ListPtr1
18618  {
18619  break;
18620  }
18621  if(ListPtr2->SubString(1, 3) == "%%%") //reached a cdt event without finding a common earlier location so go to the next ListPtr1
18622  {
18623  break;
18624  }
18625  if((*ListPtr1) == (*ListPtr2)) //found a common earlier location
18626  {
18627  Utilities->CallLogPop(2230);
18628  return( true);
18629  }
18630  }
18631  }
18632  }
18633 
18634  //now, for the departure analysis, reset the start positions and search locations after Location
18635 
18636  else
18637  {
18638  LP1 = LocPtr1;
18639  LP1++; //start at one past the location itself
18640  LP2 = LocPtr2;
18641  LP2++;
18642  for(ListPtr1 = LP1; ListPtr1 != List1.end(); ListPtr1++)
18643  {
18644  if(ListPtr1 == List1.end()) //reached end point so stop
18645  {
18646  break;
18647  }
18648  if(ListPtr1->SubString(1, 3) == "%%%") //reached a cdt event without finding a common location
18649  {
18650  break;
18651  }
18652  for(ListPtr2 = LP2; ListPtr2 != List2.end(); ListPtr2++)
18653  {
18654  if(ListPtr2 == List2.end()) //reached end point so go to next ListPtr1
18655  {
18656  break;
18657  }
18658  if(ListPtr2->SubString(1, 3) == "%%%") //reached a cdt event without finding a common location so go to the next ListPtr1
18659  {
18660  break;
18661  }
18662  if((*ListPtr1) == (*ListPtr2)) //found a common later location
18663  {
18664  Utilities->CallLogPop(2231);
18665  return( true);
18666  }
18667  }
18668  }
18669  }
18670  Utilities->CallLogPop(2232);
18671  return( false);
18672 }
18673 
18674 // ---------------------------------------------------------------------------
18675 
18676 AnsiString TTrainController::GetExitLocationAndAt(int Caller, TNumList &ExitList, AnsiString &AllowedExits) const
18677 {
18678  // changed at v2.7.0 to show allowable exit elements
18679  if(ExitList.empty())
18680  {
18681  return("");
18682  }
18683  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",GetExitLocationAndAt");
18684  AnsiString StartName = Track->TrackElementAt(735, *(ExitList.begin())).ActiveTrackElementName;
18685  AnsiString ExitLocList = "";
18686  AllowedExits = "";
18687 
18688  unsigned int Counter = 0;
18689  for(TNumListIterator ELIt = ExitList.begin(); ELIt != ExitList.end(); ELIt++)
18690  {
18691  ExitLocList += Track->TrackElementAt(1018, *ELIt).ElementID + " ";
18692  Counter++;
18693  if(((Counter % 6) == 0) && (Counter < (ExitList.size() - 1))) // only add a newline if more to come
18694  {
18695  ExitLocList += "\n";
18696  }
18697  }
18698  if(StartName == "")
18699  {
18700  if(ExitList.size() == 1)
18701  {
18702  AnsiString ID = Track->TrackElementAt(738, *(ExitList.begin())).ElementID;
18703  Utilities->CallLogPop(1571);
18704  return(" at " + ID);
18705  }
18706  else
18707  {
18708  Utilities->CallLogPop(1572);
18709  if(ExitList.size() < 4)
18710  {
18711  AllowedExits = ",\nallowable exit element(s): " + ExitLocList;
18712  return("");
18713  }
18714  else
18715  {
18716  AllowedExits = ",\nallowable exit element(s):\n" + ExitLocList;
18717  return("");
18718  }
18719  }
18720  }
18721  for(TNumListIterator ELIT = ExitList.begin(); ELIT != ExitList.end(); ELIT++)
18722  {
18723  if(Track->TrackElementAt(736, *ELIT).ActiveTrackElementName != StartName)
18724  {
18725  Utilities->CallLogPop(1570);
18726  if(ExitList.size() < 4)
18727  {
18728  AllowedExits = ",\nallowable exit element(s): " + ExitLocList;
18729  return("");
18730  }
18731  else
18732  {
18733  AllowedExits = ",\nallowable exit element(s):\n" + ExitLocList;
18734  return("");
18735  }
18736  }
18737  }
18738  Utilities->CallLogPop(1569);
18739  if(ExitList.size() < 4)
18740  {
18741  AllowedExits = ",\nallowable exit element(s): " + ExitLocList;
18742  return(" at " + StartName);
18743  }
18744  else
18745  {
18746  AllowedExits = ",\nallowable exit element(s):\n" + ExitLocList;
18747  return(" at " + StartName);
18748  }
18749 }
18750 
18751 // ---------------------------------------------------------------------------
18752 /* can't trust this as locations within a vector may not be contiguous
18753  bool TTrainController::IsServiceTerminating(int Caller, TTrainDataEntry *TDEPtr, TActionVectorEntry *AVPtr)
18754  {
18755  //Search ActionVector from the position after the entry value for Ptr to the end, and return true if find a Finish
18756  //entry before Fer or TimeLoc. No point checking for TimeTimeLoc since at a stop location now so a later TimeTimeLoc
18757  //must be preceded by a TimeLoc departure
18758  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",IsServiceTerminating");
18759  for(unsigned int x=1;x<TDEPtr->ActionVector.size();x++)
18760  {
18761  if((AVPtr + x) < TDEPtr->ActionVector.end())
18762  {
18763  AnsiString xx = (AVPtr + x)->Command;//test
18764  TTimetableFormatType xy = (AVPtr + x)->FormatType;//test
18765  TTimetableSequenceType xz = (AVPtr + x)->SequenceType;//test
18766  if(((AVPtr + x)->Command == "Fer") || ((AVPtr + x)->FormatType == TimeLoc))
18767  {
18768  Utilities->CallLogPop();
18769  return false;
18770  }
18771  else if((AVPtr + x)->SequenceType == Finish)
18772  {
18773  Utilities->CallLogPop();
18774  return true;
18775  }
18776  }
18777  }
18778  Utilities->CallLogPop();
18779  return false;
18780  }
18781 */
18782 // ---------------------------------------------------------------------------
18783 
18784 void TTrainController::SendPerformanceSummary(int Caller, std::ofstream &PerfFile)
18785 {
18786  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SendPerformanceSummary");
18787  AnsiString FormatStr = "####0.0";
18788  AnsiString AvLateArrMins = "";
18789  AnsiString AvEarlyArrMins = "";
18790  AnsiString AvLatePassMins = "";
18791  AnsiString AvEarlyPassMins = "";
18792  AnsiString AvLateDepMins = "";
18793  AnsiString AvLateExitMins = "";
18794  AnsiString AvEarlyExitMins = "";
18795 
18796  if(LateArrivals > 0)
18797  {
18798  AvLateArrMins = FormatFloat(FormatStr, (TotLateArrMins / LateArrivals));
18799  }
18800  if(EarlyArrivals > 0)
18801  {
18802  AvEarlyArrMins = FormatFloat(FormatStr, (TotEarlyArrMins / EarlyArrivals));
18803  }
18804  if(LatePasses > 0)
18805  {
18806  AvLatePassMins = FormatFloat(FormatStr, (TotLatePassMins / LatePasses));
18807  }
18808  if(EarlyPasses > 0)
18809  {
18810  AvEarlyPassMins = FormatFloat(FormatStr, (TotEarlyPassMins / EarlyPasses));
18811  }
18812  if(LateDeps > 0)
18813  {
18814  AvLateDepMins = FormatFloat(FormatStr, (TotLateDepMins / LateDeps));
18815  }
18816  if(LateExits > 0) //added at v2.9.1
18817  {
18818  AvLateExitMins = FormatFloat(FormatStr, (TotLateExitMins / LateExits));
18819  }
18820  if(EarlyExits > 0) //added at v2.9.1
18821  {
18822  AvEarlyExitMins = FormatFloat(FormatStr, (TotEarlyExitMins / EarlyExits));
18823  }
18824  PerfFile << '\n' << '\n' << "***************************************";
18825  PerfFile << '\n' << '\n' << "Performance summary:" << '\n';
18826 
18827  if(OnTimeArrivals != 1)
18828  {
18829  PerfFile << OnTimeArrivals << " on-time arrivals" << '\n';
18830  }
18831  else
18832  {
18833  PerfFile << OnTimeArrivals << " on-time arrival" << '\n';
18834  }
18835  if(LateArrivals > 1)
18836  {
18837  PerfFile << LateArrivals << " late arrivals (average " << AvLateArrMins.c_str() << " min)" << '\n';
18838  }
18839  else if(LateArrivals == 1)
18840  {
18841  PerfFile << LateArrivals << " late arrival (" << AvLateArrMins.c_str() << " min)" << '\n';
18842  }
18843  else
18844  {
18845  PerfFile << LateArrivals << " late arrivals" << '\n';
18846  }
18847  if(EarlyArrivals > 1)
18848  {
18849  PerfFile << EarlyArrivals << " early arrivals (average " << AvEarlyArrMins.c_str() << " min)" << '\n';
18850  }
18851  else if(EarlyArrivals == 1)
18852  {
18853  PerfFile << EarlyArrivals << " early arrival (" << AvEarlyArrMins.c_str() << " min)" << '\n';
18854  }
18855  else
18856  {
18857  PerfFile << EarlyArrivals << " early arrivals" << '\n';
18858  }
18859  if(OnTimePasses != 1)
18860  {
18861  PerfFile << OnTimePasses << " on-time passes" << '\n';
18862  }
18863  else
18864  {
18865  PerfFile << OnTimePasses << " on-time pass" << '\n';
18866  }
18867  if(LatePasses > 1)
18868  {
18869  PerfFile << LatePasses << " late passes (average " << AvLatePassMins.c_str() << " min)" << '\n';
18870  }
18871  else if(LatePasses == 1)
18872  {
18873  PerfFile << LatePasses << " late pass (" << AvLatePassMins.c_str() << " min)" << '\n';
18874  }
18875  else
18876  {
18877  PerfFile << LatePasses << " late passes" << '\n';
18878  }
18879  if(EarlyPasses > 1)
18880  {
18881  PerfFile << EarlyPasses << " early passes (average " << AvEarlyPassMins.c_str() << " min)" << '\n';
18882  }
18883  else if(EarlyPasses == 1)
18884  {
18885  PerfFile << EarlyPasses << " early pass (" << AvEarlyPassMins.c_str() << " min)" << '\n';
18886  }
18887  else
18888  {
18889  PerfFile << EarlyPasses << " early passes" << '\n';
18890  }
18891 
18892  if(OnTimeExits != 1) //this batch added at v2.9.1
18893  {
18894  PerfFile << OnTimeExits << " on-time exits" << '\n';
18895  }
18896  else
18897  {
18898  PerfFile << OnTimeExits << " on-time exit" << '\n';
18899  }
18900  if(LateExits > 1)
18901  {
18902  PerfFile << LateExits << " late exits (average " << AvLateExitMins.c_str() << " min)" << '\n';
18903  }
18904  else if(LateExits == 1)
18905  {
18906  PerfFile << LateExits << " late exit (" << AvLateExitMins.c_str() << " min)" << '\n';
18907  }
18908  else
18909  {
18910  PerfFile << LateExits << " late exits" << '\n';
18911  }
18912  if(EarlyExits > 1)
18913  {
18914  PerfFile << EarlyExits << " early exits (average " << AvEarlyExitMins.c_str() << " min)" << '\n';
18915  }
18916  else if(EarlyExits == 1)
18917  {
18918  PerfFile << EarlyExits << " early exit (" << AvEarlyExitMins.c_str() << " min)" << '\n';
18919  }
18920  else
18921  {
18922  PerfFile << EarlyExits << " early exits" << '\n';
18923  }
18924 
18925  if(OnTimeDeps != 1)
18926  {
18927  PerfFile << OnTimeDeps << " on-time departures" << '\n';
18928  }
18929  else
18930  {
18931  PerfFile << OnTimeDeps << " on-time departure" << '\n';
18932  }
18933  if(LateDeps > 1)
18934  {
18935  PerfFile << LateDeps << " late departures (average " << AvLateDepMins.c_str() << " min)" << '\n';
18936  }
18937  else if(LateDeps == 1)
18938  {
18939  PerfFile << LateDeps << " late departure (" << AvLateDepMins.c_str() << " min)" << '\n';
18940  }
18941  else
18942  {
18943  PerfFile << LateDeps << " late departures" << '\n';
18944  }
18945  TDateTime TempExcessLCDownTime;
18946  for(unsigned int x = 0; x < Track->BarriersDownVector.size(); x++) //added at v2.6.0 - should have been added earlier
18947  {
18948 // if(Track->BarriersDownVector.at(x).ReducedTimePenalty) //assume train still to cross LC as probably will, else have false high value & can have
18949  //later perf summaries with lower values, changed at v2.8.0
18950 // {
18951  TempExcessLCDownTime = TrainController->TTClockTime - Track->BarriersDownVector.at(x).StartTime - TDateTime(180.0 / 86400);
18952 // }
18953 /*
18954  else
18955  {
18956  TempExcessLCDownTime = TrainController->TTClockTime - Track->BarriersDownVector.at(x).StartTime;
18957  }
18958 */
18959  if(TempExcessLCDownTime > TDateTime(0))
18960  {
18961  TrainController->ExcessLCDownMins += (double(TempExcessLCDownTime) * 1440);
18962  }
18963  }
18964 
18965  AnsiString FormattedExcessLCDownMins = FormatFloat(FormatStr, ExcessLCDownMins);
18966 
18967  if(ExcessLCDownMins > 0.1)
18968  {
18969  PerfFile << FormattedExcessLCDownMins.c_str() << " excess minutes of level crossing barrier down time" << '\n';
18970  }
18971  if(MissedStops != 1)
18972  {
18973  PerfFile << MissedStops << " missed stops" << '\n';
18974  }
18975  else
18976  {
18977  PerfFile << MissedStops << " missed stop" << '\n';
18978  }
18979  if(OtherMissedEvents != 1)
18980  {
18981  PerfFile << OtherMissedEvents << " other missed events" << '\n';
18982  }
18983  else
18984  {
18985  PerfFile << OtherMissedEvents << " other missed event" << '\n';
18986  }
18987  if(UnexpectedExits != 1)
18988  {
18989  PerfFile << UnexpectedExits << " unexpected train exits" << '\n';
18990  }
18991  else
18992  {
18993  PerfFile << UnexpectedExits << " unexpected train exit" << '\n';
18994  }
18995  if(IncorrectExits != 1)
18996  {
18997  PerfFile << IncorrectExits << " incorrect train exits" << '\n';
18998  }
18999  else
19000  {
19001  PerfFile << IncorrectExits << " incorrect train exit" << '\n';
19002  }
19003  if(NumFailures != 1)
19004  {
19005  PerfFile << NumFailures << " train failures" << '\n';
19006  }
19007  else
19008  {
19009  PerfFile << NumFailures << " train failure" << '\n';
19010  }
19011  if(AvHoursIntValue > 0)
19012  {
19013  if(AvHoursIntValue == 1)
19014  {
19015  PerfFile << AvHoursIntValue << " hour mean time betweeen train failures" << '\n';
19016  }
19017  else
19018  {
19019  PerfFile << AvHoursIntValue << " hours mean time betweeen train failures" << '\n';
19020  }
19021  }
19022  AnsiString AvLateMinsLocsNotReached = "";
19023 
19025  int LocsNotReached = (NotStartedTrainLateArr + OperatingTrainLateArr); //dropped divide by 2 after 2.7.0 as don't count late departures as 'failed to arrive'
19026  // each location has an arrival and departure (generally) so divide by 2 - no, dropped after 2.7.0
19027 
19028  if(LocsNotReached > 0)
19029  {
19030  AvLateMinsLocsNotReached = FormatFloat(FormatStr, (OperatingTrainLateMins + NotStartedTrainLateMins) / (NotStartedTrainLateArr + OperatingTrainLateArr));
19031  PerfFile << LocsNotReached << " locations that trains failed to reach (average lateness " << AvLateMinsLocsNotReached.c_str() << " min)" << '\n';
19032  }
19033  if(SPADRisks != 1)
19034  {
19035  PerfFile << SPADRisks << " SPAD risks" << '\n';
19036  }
19037  else
19038  {
19039  PerfFile << SPADRisks << " SPAD risk" << '\n';
19040  }
19041  if(SPADEvents != 1)
19042  {
19043  PerfFile << SPADEvents << " SPADs" << '\n';
19044  }
19045  else
19046  {
19047  PerfFile << SPADEvents << " SPAD" << '\n';
19048  }
19049  if(Derailments != 1)
19050  {
19051  PerfFile << Derailments << " derailments" << '\n';
19052  }
19053  else
19054  {
19055  PerfFile << Derailments << " derailment" << '\n';
19056  }
19057  if(CrashedTrains != 1)
19058  {
19059  PerfFile << CrashedTrains << " crashed trains" << '\n';
19060  }
19061  else
19062  {
19063  PerfFile << CrashedTrains << " crashed train" << '\n';
19064  }
19065  PerfFile << '\n' << "***************************************" << '\n';
19066 
19067  bool DerailSPADFlag = false, CrashFlag = false;
19068 
19069  int OverallScorePercent = 100;
19070  int TotArrDepExit = 0;
19071  double TotLateMinsFactor = 1;
19072  double MissedStopAndSPADRiskFactor = 1;
19073  double NetNegFactor = 1;
19074 
19076  EarlyExits + LateExits + OnTimeExits; //exits added at v2.9.1, passes not counted
19077  // TotArrDep: total number of arrivals & departures including those for trains that haven't reached their destinations yet and are late
19078  // changed at v1.1.4 - calc was inside "if(OverallScorePercent == 100).." block so could remain 0 for SPADs & crashes, & then received the
19079  // 'no timetabled departures... message, which was inappropriate
19080 
19081  if((SPADEvents > 0) || (Derailments > 0))
19082  {
19083  OverallScorePercent = 5; // overrides other calculations
19084  DerailSPADFlag = true;
19085  }
19086  if(CrashedTrains > 0)
19087  {
19088  OverallScorePercent = 0; // overrides other calculations
19089  CrashFlag = true;
19090  }
19091  if(OverallScorePercent == 100)
19092  {
19093  if(TotArrDepExit > 0)
19094  {
19095  TotLateMinsFactor =
19097  ((OtherMissedEvents + UnexpectedExits + ExcessLCDownMins) * 15)) / TotArrDepExit); //exits added at v2.9.1
19098  // TotLateMinsFactor: negative exponential factor based on overall average arr & dep minutes late (with OtherMissedEvents & UnexpectedExits
19099  // counting as 15 mins late each), where 4 mins late average = half, 8 mins late = a quarter etc
19100  MissedStopAndSPADRiskFactor = exp((-17.33) * (MissedStops + SPADRisks + IncorrectExits) / TotArrDepExit);
19101  // MissedEventAndSPADRiskFactor: negative exponential factor based on number of missed stops, SPAD risks & IncorrectExits as a proportion
19102  // of arrivals & departures, where 4% = half, 8% = a quarter etc
19103  NetNegFactor = TotLateMinsFactor * MissedStopAndSPADRiskFactor;
19104  // NetNegfactor: product of the above two
19105  OverallScorePercent = 100 * NetNegFactor;
19106  }
19107  }
19108  if((TotArrDepExit > 0) || DerailSPADFlag || CrashFlag)
19109  // flag condits added at v1.1.4 - see above for what the error was
19110  {
19111  AnsiString OneFailureString = ", though the failure would account for some poor performance";
19112  AnsiString TwoOrMoreFailureString = ", though the failures would account for some poor performance";
19113  AnsiString AddedString = "";
19114  if(NumFailures == 1)
19115  {
19116  AddedString = OneFailureString;
19117  }
19118  if(NumFailures > 1)
19119  {
19120  AddedString = TwoOrMoreFailureString;
19121  }
19122  PerfFile << "\nOverall score: " << OverallScorePercent << "%\n";
19123  AnsiString Rating = "";
19124  if(OverallScorePercent == 100)
19125  {
19126  Rating = "Perfect!";
19127  }
19128  else if(OverallScorePercent >= 95)
19129  {
19130  Rating = "Excellent";
19131  }
19132  else if(OverallScorePercent >= 90)
19133  {
19134  Rating = "Very good";
19135  }
19136  else if(OverallScorePercent >= 80)
19137  {
19138  Rating = "Good";
19139  }
19140  else if(OverallScorePercent >= 70)
19141  {
19142  Rating = "Fair";
19143  }
19144  else if(OverallScorePercent >= 60)
19145  {
19146  Rating = "Unacceptable" + AddedString;
19147  }
19148  else if(OverallScorePercent >= 50)
19149  {
19150  Rating = "Poor" + AddedString;
19151  }
19152  else if(OverallScorePercent >= 40)
19153  {
19154  Rating = "Bad" + AddedString;
19155  }
19156  else if(OverallScorePercent >= 30)
19157  {
19158  Rating = "Very bad" + AddedString;
19159  }
19160  else if(OverallScorePercent >= 20)
19161  {
19162  Rating = "Terrible" + AddedString;
19163  }
19164  else if(OverallScorePercent >= 10)
19165  {
19166  Rating = "Appalling" + AddedString;
19167  }
19168  else if(OverallScorePercent >= 5)
19169  {
19170  if(DerailSPADFlag)
19171  {
19172  Rating = "Disastrous - potential loss of life";
19173  }
19174  // SPADs/Derailments
19175  else
19176  {
19177  Rating = "Dire" + AddedString;
19178  }
19179  }
19180  else if(OverallScorePercent < 5)
19181  {
19182  if(CrashFlag)
19183  {
19184  Rating = "Catastrophic - loss of life"; // Crashes
19185  }
19186  else
19187  {
19188  Rating = "Abysmal";
19189  }
19190  }
19191  PerfFile << "Overall rating: " << Rating.c_str() << '\n';
19192  }
19193  else
19194  {
19195  PerfFile << "\nThere were no timetabled departures, arrivals or exits so there is insufficient information to provide a performance score or rating" << '\n';
19196  }
19197  PerfFile << '\n' << "***************************************";
19198  Utilities->CallLogPop(1736);
19199 }
19200 
19201 // ---------------------------------------------------------------------------
19202 
19204 {
19205  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SetWarningFlags");
19206  for(unsigned int x = 0; x < TrainVector.size(); x++)
19207  {
19208  TTrain &Train = TrainVectorAt(58, x);
19209  if(Train.Crashed)
19210  // can't use background colours for crashed & derailed because same colour
19211  {
19212  CrashWarning = true;
19213  }
19214  else if(Train.Derailed)
19215  // can't use background colours for crashed & derailed because same colour
19216  {
19217  DerailWarning = true;
19218  }
19219  else if(Train.BackgroundColour == clSPADBackground)
19220  // use colour as that changes as soon as passes signal
19221  {
19222  SPADWarning = true;
19223  }
19224  else if(Train.BackgroundColour == clTrainFailedBackground)
19225  {
19226  TrainFailedWarning = true;
19227  }
19228  else if(Train.BackgroundColour == clCallOnBackground)
19229  // use colour as also stopped at signal
19230  {
19231  CallOnWarning = true;
19232  }
19233  else if(Train.BackgroundColour == clSignalStopBackground)
19234  // use colour to distinguish from call-on
19235  {
19236  SignalStopWarning = true;
19237  }
19238  else if(Train.BackgroundColour == clBufferAttentionNeeded)
19239  // use colour to distinguish from ordinary buffer stop
19240  {
19241  BufferAttentionWarning = true;
19242  }
19243  }
19244  Utilities->CallLogPop(1796);
19245 }
19246 
19247 // ---------------------------------------------------------------------------
19248 
19250 {
19251  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CalcSignalStopLateness");
19252 
19253  // calculate lateness for running trains
19256  for(unsigned int x = 0; x < TrainVector.size(); x++)
19257  {
19258  TTrain &Train = TrainVectorAt(64, x);
19259  for(TActionVectorEntry * AVEntryPtr = &Train.TrainDataEntryPtr->ActionVector.front(); AVEntryPtr < &Train.TrainDataEntryPtr->ActionVector.back();
19260  AVEntryPtr++)
19261  {
19262  if(AVEntryPtr < Train.ActionVectorEntryPtr)
19263  {
19264  continue;
19265  }
19266  if((AVEntryPtr->ArrivalTime > TDateTime(-1)) && !Train.StoppedAtLocation && (GetRepeatTime(42, AVEntryPtr->ArrivalTime, Train.RepeatNumber, Train.IncrementalMinutes) <
19267  TTClockTime))
19268  {
19269  OperatingTrainLateMins += 1440 * double(TTClockTime - GetRepeatTime(43, AVEntryPtr->ArrivalTime, Train.RepeatNumber, Train.IncrementalMinutes));
19271  }
19272 /* dropped departures after 2.7.0 because these don't count for 'failed to reach' numbers
19273  if((AVEntryPtr->DepartureTime > TDateTime(-1)) && (GetRepeatTime(44, AVEntryPtr->DepartureTime, Train.RepeatNumber, Train.IncrementalMinutes) <
19274  TTClockTime))
19275  {
19276  OperatingTrainLateMins += 1440 * double(TTClockTime - GetRepeatTime(45, AVEntryPtr->DepartureTime, Train.RepeatNumber, Train.IncrementalMinutes));
19277  OperatingTrainArrDep++;
19278  }
19279 */
19280  }
19281  }
19282 
19283  // calculate lateness for trains that haven't started yet (could be held awaiting entry)
19286 
19287  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
19288  {
19289  TTrainDataEntry & TDEntry = TrainDataVector.at(x);
19290  const TActionVectorEntry &AVEntryLast = TDEntry.ActionVector.at(TDEntry.ActionVector.size() - 1);
19291  int IncrementalMinutes = 0;
19292  if(AVEntryLast.FormatType == Repeat)
19293  {
19294  IncrementalMinutes = AVEntryLast.RearStartOrRepeatMins;
19295  }
19296  for(int y = 0; y < TDEntry.NumberOfTrains; y++)
19297  {
19298  TTrainOperatingData &TTOD = TDEntry.TrainOperatingDataVector.at(y);
19299  if(TTOD.RunningEntry != NotStarted)
19300  {
19301  continue;
19302  }
19303  // note that can't rely on the above for sessionfiles saved before v0.6b as wasn't set to Running for Sns/Fsp/rsp & shuttles
19304  // but if trains had exited then would be set to Exited, so need to check against trains still operating - use the test below
19305  bool TrainOperatingFlag = false;
19306  for(unsigned int a = 0; a < TrainController->TrainVector.size(); a++)
19307  {
19308  if((TrainController->TrainVector.at(a).TrainDataEntryPtr == &TDEntry) && (TrainController->TrainVector.at(a).RepeatNumber == y))
19309  {
19310  TrainOperatingFlag = true;
19311  break;
19312  }
19313  }
19314  if(TrainOperatingFlag)
19315  {
19316  continue;
19317  }
19318  if(GetRepeatTime(46, TDEntry.ActionVector.at(0).EventTime, y, IncrementalMinutes) > TTClockTime)
19319  {
19320  break; // if the first time is greater than TTClockTime then all the rest will also be greater (& default of -1 will be less so will be ignored)
19321  }
19322  for(unsigned int z = 0; z < TDEntry.ActionVector.size(); z++)
19323  {
19324  TActionVectorEntry &AVEntry = TDEntry.ActionVector.at(z);
19325  if(GetRepeatTime(35, AVEntry.EventTime, y, IncrementalMinutes) > TTClockTime)
19326  {
19327  break; // all the rest will also be greater (& default of -1 will be less)
19328  }
19329  if(GetRepeatTime(36, AVEntry.ArrivalTime, y, IncrementalMinutes) > TTClockTime)
19330  {
19331  break; // all the rest will also be greater (& default of -1 will be less)
19332  }
19333  if(GetRepeatTime(37, AVEntry.DepartureTime, y, IncrementalMinutes) > TTClockTime)
19334  {
19335  break; // all the rest will also be greater (& default of -1 will be less)
19336  }
19337  if((AVEntry.ArrivalTime > TDateTime(-1)) && (GetRepeatTime(38, AVEntry.ArrivalTime, y, IncrementalMinutes) < TTClockTime))
19338  {
19339  NotStartedTrainLateMins += 1440 * double(TTClockTime - GetRepeatTime(39, AVEntry.ArrivalTime, y, IncrementalMinutes));
19341  }
19342 /* dropped departures after 2.7.0 as only interested in 'failed to reach' number - if train hasn't arrived then it hasn't departed so shouldn't count that as part of 'failed to reach'
19343  if((AVEntry.DepartureTime > TDateTime(-1)) && (GetRepeatTime(40, AVEntry.DepartureTime, y, IncrementalMinutes) < TTClockTime))
19344  {
19345  NotStartedTrainLateMins += 1440 * double(TTClockTime - GetRepeatTime(41, AVEntry.DepartureTime, y, IncrementalMinutes));
19346  NotStartedTrainArrDep++;
19347  }
19348 */
19349  }
19350  }
19351  }
19352  Utilities->CallLogPop(1894);
19353 }
19354 
19355 // ---------------------------------------------------------------------------
19356 
19358 // new v2.2.0 for OperatorActionPanel
19359 // clears entries then adds values for running trains then for continuation entries
19360 // dont limit size here as need to check all trains (OAListBox is limited to 20 trains in Interface.cpp)
19361 {
19362  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",RebuildOpTimeToActMultimap");
19363  OpTimeToActMultiMap.clear();
19364  TOpTimeToActMultiMapEntry OpTimeToActMultiMapEntry;
19365 
19366  if(!TrainVector.empty())
19367  // build OpTimeToActMultiMap entries for running trains
19368  {
19369  AnsiString HeadCode;
19370  // dropped in favour of TrainID for running trains int VecPos; //TrackVectorPosition of LeadElement or continuation where train is to enter
19371  int TrainID;
19372  THCandTrainPosParam HCandTrainPosParam;
19373  for(unsigned int x = 0; x < TrainVector.size(); x++)
19374  {
19375  HeadCode = TrainVectorAt(62, x).HeadCode;
19376  TrainID = TrainVectorAt(63, x).TrainID;
19377  HCandTrainPosParam.first = HeadCode;
19378  HCandTrainPosParam.second = TrainID;
19379  float TimeToAct = TrainVectorAt(65, x).OpTimeToAct;
19380  if((TimeToAct >= 0) && (TimeToAct < 59.9))
19381  // -1 indicates don't display
19382  {
19383  OpTimeToActMultiMapEntry.first = TimeToAct;
19384  OpTimeToActMultiMapEntry.second = HCandTrainPosParam;
19385  OpTimeToActMultiMap.insert(OpTimeToActMultiMapEntry);
19386  }
19387  }
19388  }
19389 /*
19390  * class TContinuationTrainExpectationEntry
19391  {
19392  public:
19393  AnsiString Description; ///< service description
19394  AnsiString HeadCode; ///< service headcode
19395  int RepeatNumber; ///< service RepeatNumber
19396  int IncrementalMinutes; ///< Repeat separation in minutes
19397  int IncrementalDigits; ///< Repeat headcode separation
19398  int VectorPosition; ///< TrackVectorPosition for the continuation element
19399  TTrainDataEntry *TrainDataEntryPtr; ///< points to the service entry in the timetable's TrainDataVector
19400  };
19401 
19402  Multimap class for TContinuationTrainExpectationEntry objects, where the access key is the expectation time
19403  typedef std::multimap<TDateTime, TContinuationTrainExpectationEntry> TContinuationTrainExpectationMultiMap;
19404  typedef TContinuationTrainExpectationMultiMap::iterator TContinuationTrainExpectationMultiMapIterator; ///< iterator for the multimap
19405  typedef std::pair<TDateTime, TContinuationTrainExpectationEntry> TContinuationTrainExpectationMultiMapPair; ///< a single multimap entry
19406 */
19407 
19409  // build OpTimeToActMultiMap entries for expected trains
19410  {
19411  // note that using the ContinuationTrainExpectationMultiMap automatically ensures that entries are in ascending time order
19412  // first have to calculate times to red signal for each train due to enter (ignore later trains as will likely change before they are due)
19413  float TimeToAct = 0; // minutes
19414  int DistanceToRedSignal = 0; // metres
19415  TContinuationEntryVecPosVector ContinuationEntryVecPosVector;
19416  // used to ensure only one train displayed for a given continuation
19417  ContinuationEntryVecPosVector.clear();
19418  bool LaterTrain = false;
19421  {
19422  LaterTrain = false;
19423  if(CTEIt->second.TrainDataEntryPtr->TrainOperatingDataVector.at(CTEIt->second.RepeatNumber).RunningEntry != NotStarted)
19424  {
19425  CTEIt++;
19426  continue; // not interested in running or exited trains
19427  }
19428  if(Track->TrackElementAt(934, CTEIt->second.VectorPosition).TrainIDOnElement > 0)
19429  {
19430  CTEIt++;
19431  continue;
19432  // don't include trains not entered yet when a train is already on the continuation
19433  }
19434  if(!ContinuationEntryVecPosVector.empty())
19435  {
19436  for(unsigned int x = 0; x < ContinuationEntryVecPosVector.size(); x++)
19437  {
19438  if(CTEIt->second.VectorPosition == ContinuationEntryVecPosVector.at(x))
19439  {
19440  LaterTrain = true;
19441  ;
19442  // skip past remaining trains waiting to enter at same point
19443  break;
19444  }
19445  }
19446  }
19447  if(LaterTrain)
19448  {
19449  CTEIt++;
19450  continue;
19451  }
19452  ContinuationEntryVecPosVector.push_back(CTEIt->second.VectorPosition);
19453  AnsiString HeadCode = CTEIt->second.HeadCode;
19454  float CurrentStopTime; // set to 0 at start of function
19455  float LaterStopTime; // set to 0 at start of function
19456  float RecoverableTime; // set to 0 at start of function
19457  int AvTrackSpeed; // set to 0 at start of function
19458  int TrainID = -1; // not yet allocated for train still to enter
19459  bool SigControlAndCanPassRedSignal = false;
19460  // doesn't apply for a continuation
19461  DistanceToRedSignal = CalcDistanceToRedSignalandStopTime(1, CTEIt->second.VectorPosition, 0,
19462  // EntryPos always 0 for entering at a continuation
19463  SigControlAndCanPassRedSignal, &CTEIt->second.TrainDataEntryPtr->ActionVector.at(1),
19464  // at(1) to skip past the Start train value
19465  HeadCode, TrainID, CurrentStopTime, LaterStopTime, RecoverableTime, AvTrackSpeed);
19466  // for above VectorPosition is the first element to have its length included in the sum, so for a continuation it's the continuation itself
19467  // for a train it's the one in front of LeadElement
19468  if(AvTrackSpeed < 30)
19469  {
19470  AvTrackSpeed = 30;
19471  }
19472  if(DistanceToRedSignal == -1)
19473  {
19474  TimeToAct = 60.0;
19475  }
19476  else
19477  {
19478  int Speed = AvTrackSpeed;
19479  int MaxSpeed = int(CTEIt->second.TrainDataEntryPtr->MaxRunningSpeed);
19480  if(AvTrackSpeed > MaxSpeed)
19481  {
19482  Speed = MaxSpeed;
19483  }
19484  if(CTEIt->second.TrainDataEntryPtr->ActionVector.at(1).SignallerControl)
19485  // defined in timetable as under signaller control
19486  {
19487  Speed = CTEIt->second.TrainDataEntryPtr->SignallerSpeed;
19488  LaterStopTime = 0;
19489  }
19490  TimeToAct = LaterStopTime + DistanceToRedSignal * 3.6 / 60 / Speed;
19491  // accel & decel taken into account in
19492  // CalcDistanceToRedSignalandStopTime
19493  // 3.6 convertsKm/h to m/s & 60 converts seconds to minutes
19494  // don't need CurrentStopTime or RecoverableTime for continuation entries
19495  float MinsBefEnter = double(CTEIt->first - TTClockTime) * 86400.0 / 60.0;
19496  TimeToAct += MinsBefEnter;
19497  }
19498  THCandTrainPosParam HCandTrainPosParam;
19499  HCandTrainPosParam.first = HeadCode;
19500  HCandTrainPosParam.second = -1 - CTEIt->second.VectorPosition;
19501  // -1-CTE... because 2nd value covers TrainID if +ve &
19502  // continuation track vector position if -ve, -1 allows for vecpos being 0
19503  if(TimeToAct < 59.9) // if 60 don't enter a value in multimap
19504  {
19505  OpTimeToActMultiMapEntry.first = TimeToAct;
19506  OpTimeToActMultiMapEntry.second = HCandTrainPosParam;
19507  OpTimeToActMultiMap.insert(OpTimeToActMultiMapEntry);
19508  }
19509  CTEIt++;
19510  }
19511  }
19512  Utilities->CallLogPop(2081);
19513 }
19514 
19515 // ---------------------------------------------------------------------------
19516 
19517 int TTrainController::CalcDistanceToRedSignalandStopTime(int Caller, int TrackVectorPosition, int TrackVectorPositionEntryPos,
19518  bool SigControlAndCanPassRedSignal, TActionVectorEntry *AVPtr, AnsiString HeadCode, int TrainID, float &CurrentStopTime, float &LaterStopTime,
19519  float &RecoverableTime, int &AvTrackSpeed)
19520 // new v2.2.0
19521 // vectorPosition is the value for the first element to be measured - for a continuation it's the continuation itself, for a train
19522 // it's the one after LeadElement, returns -1 for infinity - i.e. not approaching red signal (e.g. maybe cdt before reach it).
19523 // CurrentStopTime is the time to depart from the current station (if stopped at a station) and LaterStopTime is the total station
19524 // stop times for stations after the current one. DistanceToRedSignal is what the name implies, if -1 is returned the other values
19525 // aren't used - this means there is no display for the train in question
19526 {
19527  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",DistanceToRedSignal, " + AnsiString(TrackVectorPosition) + ", " +
19528  AnsiString(TrackVectorPositionEntryPos) + ", " + AVPtr->Command);
19529  int DistanceToRedSignal = 0;
19530  int CumTrackSpeed = 0;
19531  // average track speed, in case need to use in time calc
19532  int TrackSpeedCount = 0;
19533 
19534  //below added at v2.6.1
19535  if(TrainID > -1)
19536  {
19537  TTrain &Train = TrainVectorAtIdent(51, TrainID);
19538  Train.DistanceToStationStop = 0; //if find a red signal first then this distance isn't needed
19539  Train.StationStopCalculated = false;
19540  }
19541  AvTrackSpeed = 0;
19542  int CurrentElement = TrackVectorPosition;
19543  int CurrentEntryPos = TrackVectorPositionEntryPos;
19544  int NextElement;
19545  int NextEntryPos;
19546  int NextExitPos;
19547 
19548  CurrentStopTime = 0;
19549  LaterStopTime = 0;
19550  RecoverableTime = 0;
19551  if(CurrentElement == -1) // end element, no action needed
19552  {
19553  Utilities->CallLogPop(2094);
19554  return(-1);
19555  }
19556  int CurrentExitPos;
19557 
19558  // get ExitPos for first element to be measured
19559  if(Track->TrackElementAt(935, CurrentElement).TrackType == Points)
19560  {
19561  if((CurrentEntryPos == 0) || (CurrentEntryPos == 2)) // leading point
19562  {
19563  if(Track->TrackElementAt(936, CurrentElement).Attribute == 0)
19564  {
19565  CurrentExitPos = 1;
19566  }
19567  else
19568  {
19569  CurrentExitPos = 3;
19570  }
19571  }
19572  else
19573  {
19574  CurrentExitPos = 0; // trailing point
19575  }
19576  }
19577  else
19578  {
19579  CurrentExitPos = Track->GetNonPointsOppositeLinkPos(CurrentEntryPos);
19580  }
19581  // get CumTrackSpeed for first measured element
19582 
19583  TConfiguration CurrentExitConfig = Track->TrackElementAt(937, CurrentElement).Config[CurrentExitPos];
19584  int CurrentAttribute = Track->TrackElementAt(938, CurrentElement).Attribute;
19585 
19586  // check if currently stopped at a location, and if so add the remaining dwell time
19587  // can't use CurrentElement as that is in front of LeadElement and might not be at the location
19588  if(TrainID > -1)
19589  // -1 for a continuation and can't be at a location as not yet entered
19590  {
19591  TTrain &Train = TrainVectorAtIdent(39, TrainID); //Train wasn't a reference before v2.6.1 mods so FirstLaterStopRecoverableTime wouldn't be reset for the referenced train
19593  // this used to deduct from RecoverableTime when arrive at a location
19594  if(Train.StoppedAtLocation)
19595  {
19596  if(Train.StoppedForTrainInFront)
19597  {
19598  Utilities->CallLogPop(2082);
19599  return(-1); // no action needed
19600  }
19601  else if(!((Train.ActionVectorEntryPtr->FormatType == TimeTimeLoc) || (Train.ActionVectorEntryPtr->FormatType == TimeLoc)))
19602  {
19603  Utilities->CallLogPop(2083);
19604  return(-1); // not due a departure so no action needed
19605  }
19606  else // due a departure
19607  {
19608  double TimeToDepart = double(Train.ReleaseTime - TrainController->TTClockTime) * 86400 / 60; // mins to depart
19609  // can't convert a TDateTime to a float directly
19610  CurrentStopTime = float(TimeToDepart);
19611  AVPtr++;
19612  }
19613  }
19614  }
19615  // check if CurrentElement is a red signal, but ok if autosig route after
19616  if((CurrentExitConfig == Signal) && (CurrentAttribute == 0))
19617  // ok if autosig route after red signal
19618  {
19619  int NextElement = Track->TrackElementAt(939, CurrentElement).Conn[CurrentExitPos];
19620  int NextEntryPos = Track->TrackElementAt(940, CurrentElement).ConnLinkPos[CurrentExitPos];
19621  int RouteNumber; // holder for referenced value, not used
19622  if(AllRoutes->GetRouteTypeAndNumber(33, NextElement, NextEntryPos, RouteNumber) == TAllRoutes::AutoSigsRoute)
19623  {
19624  Utilities->CallLogPop(2078);
19625  return(-1);
19626  }
19627  else if(SigControlAndCanPassRedSignal)
19628  // ignore signal and increment CurrentElement to NextElement
19629  {
19630  if(Track->TrackElementAt(941, NextElement).TrackType == Points)
19631  {
19632  if((NextEntryPos == 0) || (NextEntryPos == 2))
19633  // leading entry point
19634  {
19635  if(Track->TrackElementAt(942, NextElement).Attribute == 0)
19636  {
19637  NextExitPos = 1;
19638  }
19639  else
19640  {
19641  NextExitPos = 3;
19642  }
19643  }
19644  else
19645  {
19646  NextExitPos = 0; // trailing entry point
19647  }
19648  }
19649  else
19650  {
19651  NextExitPos = Track->GetNonPointsOppositeLinkPos(NextEntryPos);
19652  }
19653  CurrentElement = NextElement;
19654  CurrentEntryPos = NextEntryPos;
19655  CurrentExitPos = NextExitPos;
19656  CurrentExitConfig = Track->TrackElementAt(943, CurrentElement).Config[CurrentExitPos];
19657  CurrentAttribute = Track->TrackElementAt(944, CurrentElement).Attribute;
19658  }
19659  else if((TrainID > -1) && (TrainVectorAtIdent(40, TrainID).TrainMode == Timetable)) // ignore signallercontrol or will
19660  // give 'NOW' indication after allowed to pass red signal when LeadMidLag (AllowedToPassRedSignal reset by this point)
19661  {
19662  Utilities->CallLogPop(2084);
19663  return(0);
19664  // stopped with red signal in front, don't need AvSpeedLimit in this case, & if at location awaiting departure dwell time already calculated
19665  }
19666  }
19667  int LaterStopNumber = 0;
19668  int x = 0;
19669  // added in v2.4.0 to prevent endless circling round track loops - spotted by Xeon 09/03/20 & reported by emsil
19670 
19671  while(!((CurrentExitConfig == Signal) && (CurrentAttribute == 0)))
19672  // not red signal next (in fwd direction) so enter loop to calc CumLength
19673  {
19674  x++; // added in v2.4.0 as above
19675  if(x > 5000)
19676  {
19677  Utilities->CallLogPop(2120);
19678  return(-1);
19679  }
19680  if(CurrentEntryPos > 1)
19681  {
19682  DistanceToRedSignal += Track->TrackElementAt(916, CurrentElement).Length23;
19683  CumTrackSpeed += Track->TrackElementAt(945, CurrentElement).SpeedLimit23;
19684  }
19685  else
19686  {
19687  DistanceToRedSignal += Track->TrackElementAt(917, CurrentElement).Length01;
19688  CumTrackSpeed += Track->TrackElementAt(946, CurrentElement).SpeedLimit01;
19689  }
19690  TrackSpeedCount++;
19691 
19692  // added at v2.6.1 to find DistanceToStationStop for trains running early
19693  if(TrainID > -1) //can ignore continuation entries as these don't run early
19694  {
19695  TTrain &Train = TrainVectorAtIdent(52, TrainID);
19696  if(!Train.StationStopCalculated)
19697  {
19698  if(Train.TrainMode == Timetable)
19699  {
19700  bool StopRequired = false;
19701  if(!Train.TimetableFinished && (Train.NameInTimetableBeforeCDT(16, Track->TrackElementAt(1005, CurrentElement).ActiveTrackElementName,
19702  StopRequired) > -1) && ((Track->TrackElementAt(1006, CurrentElement).StationEntryStopLinkPos1 == CurrentEntryPos) ||
19703  (Track->TrackElementAt(1010, CurrentElement).StationEntryStopLinkPos2 == CurrentEntryPos)))
19704  {
19705  // no need to add in the length of element to CumulativeLength
19706  if(StopRequired)
19707  {
19708  Train.DistanceToStationStop = DistanceToRedSignal; // DistanceToRedSignal holds the intermediate distance to this point
19709  Train.StationStopCalculated = true; //don't want to update it with later stops
19710  }
19711  }
19712  }
19713  }
19714  }
19715  // check for train in front, but if on a bridge on other track then ok
19716  TTrackElement TE = Track->TrackElementAt(947, CurrentElement);
19717  int TrainOnElement;
19718  if(TE.TrackType != Bridge)
19719  {
19720  TrainOnElement = TE.TrainIDOnElement;
19721  }
19722  else
19723  {
19724  if(CurrentEntryPos > 1)
19725  {
19726  TrainOnElement = TE.TrainIDOnBridgeTrackPos23;
19727  }
19728  else
19729  {
19730  TrainOnElement = TE.TrainIDOnBridgeTrackPos01;
19731  }
19732  }
19733  if((TrainOnElement > -1) && (TrainOnElement != TrainID))
19734  // train in front before red signal
19735  {
19736  Utilities->CallLogPop(2085);
19737  return(-1);
19738  }
19739  // add to stoptime if required
19740  if(Track->TrackElementAt(948, CurrentElement).ActiveTrackElementName != "")
19741  {
19742  double StopTimeDouble;
19743  while(AVPtr->FormatType == PassTime)
19744  {
19745  AVPtr++; // skip past any passes
19746  }
19747  if((Track->TrackElementAt(949, CurrentElement).ActiveTrackElementName == AVPtr->LocationName) && ((AVPtr->FormatType == TimeLoc) ||
19748  (AVPtr->FormatType == TimeTimeLoc)))
19749  // stop due here so calc dwell time & advance Ptr
19750  {
19751  if(AVPtr->FormatType == TimeTimeLoc)
19752  {
19753  LaterStopNumber++;
19754  StopTimeDouble = double(AVPtr->DepartureTime - AVPtr->ArrivalTime) * 86400.0 / 60.0;
19755  if(StopTimeDouble < 0.5)
19756  {
19757  StopTimeDouble = 0.5;
19758  }
19759  // at least 30 secs delay at station
19760  // can't convert a TDateTime to a float directly
19761  LaterStopTime += float(StopTimeDouble);
19762  RecoverableTime += StopTimeDouble - 0.5;
19763  if((LaterStopNumber == 1) && (TrainID > -1))
19764  {
19765  TrainVectorAtIdent(41, TrainID).FirstLaterStopRecoverableTime = RecoverableTime;
19766  }
19767  AVPtr++;
19768  }
19769  else if((AVPtr->FormatType == TimeLoc) && (AVPtr->ArrivalTime != TDateTime(-1))) // must be an arrival
19770  {
19771  if((AVPtr + 1)->FormatType == TimeLoc)
19772  // must be a departure
19773  {
19774  LaterStopNumber++;
19775  StopTimeDouble = double((AVPtr + 1)->DepartureTime - AVPtr->ArrivalTime) * 86400.0 / 60.0;
19776  // can't convert a TDateTime to a float directly
19777  if(StopTimeDouble < 0.5)
19778  {
19779  StopTimeDouble = 0.5;
19780  }
19781  // at least 30 secs delay at station
19782  LaterStopTime += float(StopTimeDouble);
19783  RecoverableTime += StopTimeDouble - 0.5;
19784  if((LaterStopNumber == 1) && (TrainID > -1))
19785  {
19786  TrainVectorAtIdent(42, TrainID).FirstLaterStopRecoverableTime = RecoverableTime;
19787  }
19788  AVPtr++;
19789  AVPtr++;
19790  }
19791  else // not a departure, does something else at the location so no calculation needed
19792  {
19793  Utilities->CallLogPop(2086);
19794  return(-1);
19795  }
19796  }
19797  }
19798  }
19799  NextElement = Track->TrackElementAt(950, CurrentElement).Conn[CurrentExitPos];
19800  if(NextElement == -1) // reached end element, no action needed
19801  {
19802  Utilities->CallLogPop(2077);
19803  return(-1);
19804  }
19805  NextEntryPos = Track->TrackElementAt(919, CurrentElement).ConnLinkPos[CurrentExitPos];
19806  // get NextExitPos
19807  if(Track->TrackElementAt(920, NextElement).TrackType == Points)
19808  {
19809  if((NextEntryPos == 0) || (NextEntryPos == 2))
19810  // leading entry point
19811  {
19812  if(Track->TrackElementAt(921, NextElement).Attribute == 0)
19813  {
19814  NextExitPos = 1;
19815  }
19816  else
19817  {
19818  NextExitPos = 3;
19819  }
19820  }
19821  else
19822  {
19823  NextExitPos = 0; // trailing entry point
19824  }
19825  }
19826  else
19827  {
19828  NextExitPos = Track->GetNonPointsOppositeLinkPos(NextEntryPos);
19829  }
19830  CurrentElement = NextElement;
19831  CurrentEntryPos = NextEntryPos;
19832  CurrentExitPos = NextExitPos;
19833  CurrentExitConfig = Track->TrackElementAt(922, CurrentElement).Config[CurrentExitPos];
19834  CurrentAttribute = Track->TrackElementAt(923, CurrentElement).Attribute;
19835  }
19836  if((CurrentExitConfig == Signal) && (CurrentAttribute == 0))
19837  // ok if autosig route after red signal, no action needed
19838  {
19839  int NextElement = Track->TrackElementAt(924, CurrentElement).Conn[CurrentExitPos];
19840  int NextEntryPos = Track->TrackElementAt(925, CurrentElement).ConnLinkPos[CurrentExitPos];
19841  int RouteNumber; // holder for referenced value, not used
19842  if(AllRoutes->GetRouteTypeAndNumber(31, NextElement, NextEntryPos, RouteNumber) == TAllRoutes::AutoSigsRoute)
19843  {
19844  Utilities->CallLogPop(2095);
19845  return(-1);
19846  }
19847  }
19848  // Av speed calculation based on formula: Speed = 8.75*(kms/location stop) + 44 km/sec (from experiments), subject to a maximum of
19849  // average line speed/2 (for half distance accelerating and half decelerating.
19850 
19851  float MaxAllowableSpeed;
19852 
19853  if(TrackSpeedCount > 0)
19854  {
19855  MaxAllowableSpeed = CumTrackSpeed / TrackSpeedCount;
19856  }
19857  else // shouldn't reach here but include to prevent divide by zero error
19858  {
19859  if(CurrentEntryPos > 1)
19860  {
19861  MaxAllowableSpeed = Track->TrackElementAt(951, CurrentElement).SpeedLimit23;
19862  }
19863  else
19864  {
19865  MaxAllowableSpeed = Track->TrackElementAt(952, CurrentElement).SpeedLimit01;
19866  }
19867  // Train MaxRunningSpeed taken into account in RebuildOpTimeToActMultimap
19868  }
19869  float KmPerLocationStop;
19870 
19871  if(LaterStopNumber > 0)
19872  {
19873  KmPerLocationStop = float(DistanceToRedSignal) / LaterStopNumber / 1000; // m to km
19874  AvTrackSpeed = (8.75 * KmPerLocationStop) + 44;
19875  }
19876  else
19877  {
19878  AvTrackSpeed = (sqrt(float(DistanceToRedSignal) / 1000) * 44) + 60;
19879  // using linear trendline for accel & decel distance at various speeds
19880  // at half braking, speed never < 60 using this
19881  }
19882  if(AvTrackSpeed > MaxAllowableSpeed)
19883  {
19884  AvTrackSpeed = MaxAllowableSpeed;
19885  }
19886  Utilities->CallLogPop(2096);
19887  return(DistanceToRedSignal);
19888 }
19889 
19890 // ---------------------------------------------------------------------------
19891 // end of TTrainController entries
19892 // ---------------------------------------------------------------------------
TTrain::LinkOccupied
bool LinkOccupied(int Caller, int TrackVectorPosition, int LinkNumber)
Added at v1.2.0: true if any part of train on specific link, false otherwise, including no link prese...
Definition: TrainUnit.cpp:8375
TAllRoutes::TrackIsInARoute
bool TrackIsInARoute(int Caller, int TrackVectorPosition, int LinkPos)
Examines Route2MultiMap and if the element at TrackVectorPosition with LinkPos (can be entry or exit)...
Definition: TrackUnit.cpp:17550
TActionVectorEntry::EventTime
TDateTime EventTime
Definition: TrainUnit.h:115
TTrain::AllowedToPassRedSignal
bool AllowedToPassRedSignal
set when train has been called on, or when under signaller control and instructed to pass a red signa...
Definition: TrainUnit.h:349
JoinedByOther
@ JoinedByOther
Definition: TrainUnit.h:50
TTrainController::CheckShuttleRepeatTime
bool CheckShuttleRepeatTime(int Caller, TDateTime ForwardEventTime, TDateTime ReverseEventTime, int RepeatMinutes)
Check that shuttle link services have consistent times, true for success.
Definition: TrainUnit.cpp:14165
TTrain::ZeroPowerNoNewShuttleFromNonRepeatMessage
bool ZeroPowerNoNewShuttleFromNonRepeatMessage
Definition: TrainUnit.h:316
TTrain::ZeroPowerNoNewServiceMessage
bool ZeroPowerNoNewServiceMessage
Definition: TrainUnit.h:315
TTrain::VOffset
int VOffset[4]
each headcode character is an 8x8 pixel graphic and must be placed within a 16x16 pixel element,...
Definition: TrainUnit.h:445
TTrainController
Handles all train and timetable activities, only one object created.
Definition: TrainUnit.h:648
TTrain::TTrain
TTrain(int Caller, int RearStartElementIn, int RearStartExitPosIn, AnsiString InputCode, int StartSpeed, int Mass, double MaxRunningSpeed, double MaxBrakeRate, double PowerAtRail, TTrainMode TrainMode, TTrainDataEntry *TrainDataEntryPtr, int RepeatNumber, int IncrementalMinutes, int IncrementalDigits, int SignallerMaxSpeed)
Constructor, sets listed member values.
Definition: TrainUnit.cpp:62
TAllRoutes::LockedRouteVector
TLockedRouteVector LockedRouteVector
the vector that stores all the locked routes on the railway
Definition: TrackUnit.h:1702
TRailGraphics::smOrange
Graphics::TBitmap * smOrange
Definition: GraphicUnit.h:885
TActionVector
std::vector< TActionVectorEntry > TActionVector
contains all actions for a single train
Definition: TrainUnit.h:153
TUtilities::LoadFileString
AnsiString LoadFileString(std::ifstream &InFile)
loads a string value from the file
Definition: Utilities.cpp:190
TTrainController::CheckNonRepeatingShuttleLinksAndSetData
bool CheckNonRepeatingShuttleLinksAndSetData(int Caller, AnsiString MainHeadCode, AnsiString NonRepeatingHeadCode, bool GiveMessages)
A timetable validation function where cross references are checked for validity for non-repeating shu...
Definition: TrainUnit.cpp:14188
TTrainController::CheckStartPositionValidity
bool CheckStartPositionValidity(int Caller, AnsiString RearElementStr, AnsiString FrontElementStr, bool GiveMessages)
A timetable validation function where train starting positions are checked for validity,...
Definition: TrainUnit.cpp:13832
TTrain::MidEntryPos
int MidEntryPos
Definition: TrainUnit.h:337
TTrainController::RebuildOpTimeToActMultimap
void RebuildOpTimeToActMultimap(int Caller)
new v2.2.0 for OperatorActionPanel
Definition: TrainUnit.cpp:19357
TTrainController::PwrHigh
bool PwrHigh
Definition: TrainUnit.h:756
TTrainController::BuildContinuationTrainExpectationMultiMap
void BuildContinuationTrainExpectationMultiMap(int Caller)
populate the ContinuationTrainExpectationMultiMap during timetable loading
Definition: TrainUnit.cpp:15230
TTrain::CheckNewServiceDepartureTime
AnsiString CheckNewServiceDepartureTime(int Caller, TActionVectorEntry *Ptr, int RptNum, TTrainDataEntry *LinkedTrainDataPtr, AnsiString RetStr)
called during FloatingLabelNextString to find the next service departure time
Definition: TrainUnit.cpp:7005
TFixedTrackPiece::GraphicPtr
Graphics::TBitmap * GraphicPtr
the track bitmap for display on the zoomed-in railway
Definition: TrackUnit.h:93
SignallerMoveForwards
@ SignallerMoveForwards
Definition: TrainUnit.h:51
TRailGraphics::CodeR
Graphics::TBitmap * CodeR
Definition: GraphicUnit.h:981
Create
@ Create
Definition: TrainUnit.h:50
TTrainController::TContinuationEntryVecPosVector
std::vector< int > TContinuationEntryVecPosVector
ensures only one train displayed for a given continuation
Definition: TrainUnit.h:735
TAllRoutes::SetAllRearwardsSignals
void SetAllRearwardsSignals(int Caller, int Attribute, int RouteNumber, int RouteStartPosition)
Set rearwards signals from the specified route starting position.
Definition: TrackUnit.cpp:18556
Arrive
@ Arrive
Definition: TrainUnit.h:50
ChangeDirection
@ ChangeDirection
Definition: TrainUnit.h:50
TTrain::ZeroPowerNoCDTMessage
bool ZeroPowerNoCDTMessage
Definition: TrainUnit.h:314
TTrain::CallingOnFlag
bool CallingOnFlag
calling on permitted
Definition: TrainUnit.h:355
Depart
@ Depart
Definition: TrainUnit.h:50
TRailGraphics::gl89set
Graphics::TBitmap * gl89set
Definition: GraphicUnit.h:702
TTrainController::CheckHeadCodeValidity
bool CheckHeadCodeValidity(int Caller, bool GiveMessages, AnsiString HeadCode)
Returns true if the headcode complies with requirements.
Definition: TrainUnit.cpp:11041
TRailGraphics::gl88set
Graphics::TBitmap * gl88set
Definition: GraphicUnit.h:700
clBufferStopBackground
#define clBufferStopBackground
Definition: GraphicUnit.h:291
TTrain::MaxRunningSpeed
double MaxRunningSpeed
the current maximum train running speed
Definition: TrainUnit.h:395
TTrack::BarriersDownVector
TActiveLCVector BarriersDownVector
vector of LCs with barriers down
Definition: TrackUnit.h:794
TPrefDirElement::GetXLinkPos
int GetXLinkPos() const
Returns the XLink array position.
Definition: TrackUnit.h:299
TTrack::IsLCBarrierDownAtHV
bool IsLCBarrierDownAtHV(int Caller, int HLoc, int VLoc)
True if an open (to trains) level crossing is found at H & V.
Definition: TrackUnit.cpp:7137
TAllRoutes::TLockedRouteClass::TruncateTrackVectorPosition
unsigned int TruncateTrackVectorPosition
the TrackVector position of the element selected for truncation
Definition: TrackUnit.h:1619
RestoreTimetableControl
@ RestoreTimetableControl
Definition: TrainUnit.h:51
FailCrashed
@ FailCrashed
Definition: TrainUnit.h:41
TRailGraphics::TempHeadCode
Graphics::TBitmap * TempHeadCode
Definition: GraphicUnit.h:891
TAllRoutes::AutoSigsRoute
@ AutoSigsRoute
Definition: TrackUnit.h:1631
TTrack::TSigElement::Attribute
int Attribute
the signal state - red, yellow, double yellow or green
Definition: TrackUnit.h:728
TActionVectorEntry::LocationType
TTimetableLocationType LocationType
indicates where the train is when the relevant action occurs
Definition: TrainUnit.h:121
TTrack::GapFlashGreenPosition
int GapFlashGreenPosition
Definition: TrackUnit.h:778
TAllRoutes::TRouteElementPair
std::pair< int, unsigned int > TRouteElementPair
defines a specific element in a route, the first (int) value is the vector position in the AllRoutesV...
Definition: TrackUnit.h:1643
NamedNonStationLocation
@ NamedNonStationLocation
Definition: TrackUnit.h:68
FinRemHere
@ FinRemHere
Definition: TrainUnit.h:64
TTrain::HeadCodeGrPtr
Graphics::TBitmap * HeadCodeGrPtr[4]
points to the headcode segment graphics e.g. 5,A,4,7.
Definition: TrainUnit.h:462
TTrainController::TContinuationTrainExpectationEntry::TrainDataEntryPtr
TTrainDataEntry * TrainDataEntryPtr
points to the service entry in the timetable's TrainDataVector
Definition: TrainUnit.h:696
TTrain::ChangeTrainDirection
void ChangeTrainDirection(int Caller)
Reverses the direction of motion of the train.
Definition: TrainUnit.cpp:6163
FSHNewService
@ FSHNewService
Definition: TrainUnit.h:65
TTrain::MaxBrakeRate
double MaxBrakeRate
the maximum brake rate that the train can achieve
Definition: TrainUnit.h:399
TTimetableLocationType
TTimetableLocationType
Definition: TrainUnit.h:69
TAllRoutes::DiagonalFouledByRoute
bool DiagonalFouledByRoute(int Caller, int HLoc, int VLoc, int DiagonalLinkNumber)
As above but only checks for a route (may or may not be a train present (new at v1....
Definition: TrackUnit.cpp:19305
TTrainController::CheckForDuplicateCrossReferences
bool CheckForDuplicateCrossReferences(int Caller, AnsiString MainHeadCode, AnsiString SecondHeadCode, bool GiveMessages)
A timetable validation function where referenced services are checked for uniqueness,...
Definition: TrainUnit.cpp:13116
TTrackElement::StationEntryStopLinkPos2
int StationEntryStopLinkPos2
Used for track at platforms and non-station named locations to mark the train front element stop posi...
Definition: TrackUnit.h:152
TTrain::MidExitPos
int MidExitPos
Definition: TrainUnit.h:337
TRailGraphics::CodeD
Graphics::TBitmap * CodeD
Definition: GraphicUnit.h:967
TTrain::TrainFailed
bool TrainFailed
added at v2.4.0 to indicate failure
Definition: TrainUnit.h:381
TUtilities::CheckFileStringZeroDelimiter
bool CheckFileStringZeroDelimiter(std::ifstream &InFile)
checks that the value is a string ('0' only accepted as the delimiter), returns true for success
Definition: Utilities.cpp:435
DisplayUnit.h
TRailGraphics::Code_w
Graphics::TBitmap * Code_w
Definition: GraphicUnit.h:950
FailDerailed
@ FailDerailed
Definition: TrainUnit.h:41
TTrainController::SPADWarning
bool SPADWarning
Definition: TrainUnit.h:742
TTrain::ZeroPowerNoFrontSplitMessage
bool ZeroPowerNoFrontSplitMessage
Definition: TrainUnit.h:310
TRailGraphics::Code_s
Graphics::TBitmap * Code_s
Definition: GraphicUnit.h:946
TTrainController::GetExitLocationAndAt
AnsiString GetExitLocationAndAt(int Caller, TNumList &ExitList, AnsiString &AllowedExits) const
Check all timetable names in ExitList, if all same return " at [name]" + AllowableExits = elements,...
Definition: TrainUnit.cpp:18676
TTrain::SaveOneSessionTrain
void SaveOneSessionTrain(int Caller, std::ofstream &OutFile)
Data for a single train is saved to a session file.
Definition: TrainUnit.cpp:7251
TTrain::DepartureTimeSet
bool DepartureTimeSet
set when stopped at a location and the next action is departure (set in UpdateTrain when ReleaseTime ...
Definition: TrainUnit.h:357
TTrain::Plotted
bool Plotted
set when train plotted on screen
Definition: TrainUnit.h:436
TTrain::LagElement
int LagElement
Definition: TrainUnit.h:337
TTrain::RemainHere
void RemainHere(int Caller)
Sends the 'train terminated' message to the performance log and sets TimetableFinished to true.
Definition: TrainUnit.cpp:6277
TTrainController::BufferAttentionWarning
bool BufferAttentionWarning
Definition: TrainUnit.h:742
TTrainController::TrainVectorAtIdent
TTrain & TrainVectorAtIdent(int Caller, int TrainID)
Return a reference to the train with ID TrainID, carries out validity checking on TrainID.
Definition: TrainUnit.cpp:9441
TPrefDirElement::GetRouteEXGraphicPtr
Graphics::TBitmap * GetRouteEXGraphicPtr()
Returns route graphic.
Definition: TrackUnit.h:335
TRailGraphics::smLightBlue
Graphics::TBitmap * smLightBlue
Definition: GraphicUnit.h:882
TTrainController::TContinuationAutoSigEntry::RouteNumber
int RouteNumber
the AllRoutesVector position of the route that the continuation is in
Definition: TrainUnit.h:670
TTrainController::StopTTClockMessage
void StopTTClockMessage(int Caller, AnsiString Message)
sends a message to the user and stops the timetable clock while it is displayed
Definition: TrainUnit.cpp:14961
TTrainController::OperatingTrainLateArr
int OperatingTrainLateArr
< all these set to 0 in constructor
Definition: TrainUnit.h:800
TRailGraphics::CodeJ
Graphics::TBitmap * CodeJ
Definition: GraphicUnit.h:973
TUtilities::IncrementAnsiTimeOneMinute
AnsiString IncrementAnsiTimeOneMinute(AnsiString TimeVal)
takes "HH:MM" and increments it to "HH:MX", where MX == MM + 1, incrementing the hour if necessary
Definition: Utilities.cpp:619
TRailGraphics::CodeQ
Graphics::TBitmap * CodeQ
Definition: GraphicUnit.h:980
TDisplay::GetOutputLog9
TLabel * GetOutputLog9()
Definition: DisplayUnit.h:184
TAllRoutes::RemoveRouteElement
void RemoveRouteElement(int Caller, int HLoc, int VLoc, int ELink)
Erases the route element from Route2MultiMap and from the PrefDirVector.
Definition: TrackUnit.cpp:18346
TTrainController::CrashWarning
bool CrashWarning
Definition: TrainUnit.h:742
TTrainController::CheckLocationValidity
bool CheckLocationValidity(int Caller, AnsiString LocStr, bool GiveMessages, bool CheckLocationsExistInRailway)
Returns true if the location name complies with requirements.
Definition: TrainUnit.cpp:10994
TAllRoutes::TLockedRouteClass::LastXLinkPos
int LastXLinkPos
the XLinkPos value of the last (i.e. most forward) element in the route
Definition: TrackUnit.h:1623
TTrain::PlotTrain
void PlotTrain(int Caller, TDisplay *Disp)
Plots the train on the display in normal (zoomed-in) mode.
Definition: TrainUnit.cpp:8347
TNumListIterator
TNumList::iterator TNumListIterator
Definition: TrainUnit.h:95
FailLevelCrossingCrash
@ FailLevelCrossingCrash
Definition: TrainUnit.h:43
TActionVectorEntry::DepartureTime
TDateTime DepartureTime
relevant times at which the action is timetabled, zeroed on creation so change to -1 as a marker for ...
Definition: TrainUnit.h:115
TRailGraphics::Code_f
Graphics::TBitmap * Code_f
Definition: GraphicUnit.h:933
FailCreateLockedRoute
@ FailCreateLockedRoute
Definition: TrainUnit.h:42
TTrainController::TContinuationTrainExpectationEntry::IncrementalDigits
int IncrementalDigits
Repeat headcode separation.
Definition: TrainUnit.h:692
TTrainController::SaveSessionTrains
void SaveSessionTrains(int Caller, std::ofstream &SessionFile)
save trains to a session file
Definition: TrainUnit.cpp:14976
TOneCompleteFormattedTrain::HeadCode
AnsiString HeadCode
Definition: TrainUnit.h:244
Intermediate
@ Intermediate
Definition: TrainUnit.h:75
TRailGraphics::Code_d
Graphics::TBitmap * Code_d
Definition: GraphicUnit.h:931
TTrain::JoinedBy
void JoinedBy(int Caller)
Carry out the actions needed when a train is waiting to be joined by another train.
Definition: TrainUnit.cpp:6092
FailIncorrectExit
@ FailIncorrectExit
Definition: TrainUnit.h:43
TRailGraphics::Code_t
Graphics::TBitmap * Code_t
Definition: GraphicUnit.h:947
TRailGraphics::ChangeBackgroundColour
void ChangeBackgroundColour(int Caller, Graphics::TBitmap *BitmapIn, Graphics::TBitmap *BitmapOut, TColor NewBackgroundColour, TColor OldBackgroundColour, bool &ColourError)
Definition: GraphicUnit.cpp:3438
TTrain::GetLeadElement
int GetLeadElement()
get LeadElement - used in RouteLockingRequired in TrackUnit.cpp
Definition: TrainUnit.h:630
TimeCmd
@ TimeCmd
Definition: TrainUnit.h:64
TTrain::BackgroundPtr
Graphics::TBitmap * BackgroundPtr[4]
the existing track graphic that the train headcode segment covers up (one for each headcode segment)
Definition: TrainUnit.h:458
TUtilities::LoadFileDouble
double LoadFileDouble(std::ifstream &InFile)
loads a double value from the file (converts from a string to a double) and uses the local decimal po...
Definition: Utilities.cpp:172
TTrain::DistanceToStationStop
int DistanceToStationStop
calculated in UpdateTrain & used in CalcDistanceToRedSignalandStopTime to cater for trains running ea...
Definition: TrainUnit.h:420
TTrainController::EntryPos
int EntryPos(int Caller, int TrainIDIn, int TrackVectorNumber)
Return the track entry link (Link[]) array position for the given train on track element at track vec...
Definition: TrainUnit.cpp:9403
SignallerLeave
@ SignallerLeave
Definition: TrainUnit.h:52
TTrain::PlotEntryPos
int PlotEntryPos[4]
the LinkPos value corresponding to the train entry link of the element where each of the 4 headcode c...
Definition: TrainUnit.h:451
NotStarted
@ NotStarted
Definition: TrainUnit.h:86
TTrack::OneNamedLocationElementAtLocation
bool OneNamedLocationElementAtLocation(int Caller, AnsiString LocationName)
True if there is at least one named location element with name 'LocationName', used in timetable inte...
Definition: TrackUnit.cpp:10705
TTrainController::SPADEvents
int SPADEvents
Definition: TrainUnit.h:794
TTrainController::LastTTTime
AnsiString LastTTTime
Stores the last time used in the timetable as an AnsiString - used for timetable analysis.
Definition: TrainUnit.h:738
TTrain::ZeroPowerNoRepeatShuttleMessage
bool ZeroPowerNoRepeatShuttleMessage
Definition: TrainUnit.h:317
TTrackElement::SigAspect
enum TTrackElement::@1 SigAspect
TRailGraphics::gl90set
Graphics::TBitmap * gl90set
Definition: GraphicUnit.h:705
TTrain::ActionVectorEntryPtr
TActionVectorEntry * ActionVectorEntryPtr
points to the current position in the ActionVector (a member of the TTrainDataEntry class)
Definition: TrainUnit.h:345
TTrainDataEntry
Contains all data for a single timetable service entry.
Definition: TrainUnit.h:186
LeadMid
@ LeadMid
Definition: TrainUnit.h:277
FailMissedPass
@ FailMissedPass
Definition: TrainUnit.h:42
clSignalStopBackground
#define clSignalStopBackground
Definition: GraphicUnit.h:299
TTrainController::LateDeps
int LateDeps
Definition: TrainUnit.h:785
TAllRoutes::IsElementInLockedRouteGetPrefDirElementGetLockedVectorNumber
bool IsElementInLockedRouteGetPrefDirElementGetLockedVectorNumber(int Caller, int TrackVectorPosition, int XLinkPos, TPrefDirElement &PrefDirElement, int &LockedVectorNumber)
Checks whether the preferred direction element at TrackVectorPosition with XLinkPos value is in a loc...
Definition: TrackUnit.cpp:18820
TrackUnit.h
TTrainController::CheckStartAllowable
bool CheckStartAllowable(int Caller, int RearPosition, int RearExitPos, AnsiString HeadCode, bool ReportFlag, TActionEventType &EventType)
Called when trying to introduce a new train - checks for points in correct orientation,...
Definition: TrainUnit.cpp:13912
TRailGraphics::Code_r
Graphics::TBitmap * Code_r
Definition: GraphicUnit.h:945
TTrain::SendMissedActionLogs
void SendMissedActionLogs(int Caller, int IncNum, TActionVectorEntry *Ptr)
Missed actions (see NameInTimetableBeforeCDT above) sent to the performance log and performance file.
Definition: TrainUnit.cpp:6298
TTrainController::TContinuationTrainExpectationEntry::RepeatNumber
int RepeatNumber
service RepeatNumber
Definition: TrainUnit.h:688
TRailGraphics::CodeX
Graphics::TBitmap * CodeX
Definition: GraphicUnit.h:987
TOneTrainFormattedEntry::Time
AnsiString Time
the time of the action as a string
Definition: TrainUnit.h:231
TRailGraphics::CodeT
Graphics::TBitmap * CodeT
Definition: GraphicUnit.h:983
TTrainDataEntry::PowerAtRail
double PowerAtRail
in Watts (taken as 80% of the train's Gross Power, i.e. that entered by the user)
Definition: TrainUnit.h:194
TTrainController::LastTrainLoaded
int LastTrainLoaded
displays last train loaded from session file, used for debugging
Definition: TrainUnit.h:804
TTrainController::NotStartedTrainLateMins
float NotStartedTrainLateMins
total late minutes of trains that haven't started yet on exit operation for locations not reached yet
Definition: TrainUnit.h:762
Utilities.h
TTrainController::TContinuationAutoSigVectorIterator
TContinuationAutoSigVector::iterator TContinuationAutoSigVectorIterator
Definition: TrainUnit.h:678
TTrain::TrainOnContinuation
bool TrainOnContinuation(int Caller)
Returns true if any part of train on a continuation - called when checking for failures,...
Definition: TrainUnit.cpp:8653
TTrainController::CheckSessionLockedRoutes
bool CheckSessionLockedRoutes(int Caller, std::ifstream &SessionFile)
Part of the session file integrity check for locked routes, true for success.
Definition: TrainUnit.cpp:15079
FailTrainEntry
@ FailTrainEntry
Definition: TrainUnit.h:40
MidLag
@ MidLag
Definition: TrainUnit.h:277
TDisplay::Update
void Update()
Repaint the screen display.
Definition: DisplayUnit.h:221
TTrainController::LocServiceTimesDepTimeSort
bool LocServiceTimesDepTimeSort(TLocServiceTimes i, TLocServiceTimes j)
Definition: TrainUnit.h:841
TTrainController::CheckSessionTrains
bool CheckSessionTrains(int Caller, std::ifstream &InFile)
Part of the session file integrity check for train entries, true for success.
Definition: TrainUnit.cpp:15017
StartNew
@ StartNew
Definition: TrainUnit.h:64
TakeSignallerControl
@ TakeSignallerControl
Definition: TrainUnit.h:50
LeadMidLag
@ LeadMidLag
Definition: TrainUnit.h:277
TTrack::GapFlashGreen
TGraphicElement * GapFlashGreen
Definition: TrackUnit.h:798
TTrain::ZeroPowerNoRepeatShuttleOrNewServiceMessage
bool ZeroPowerNoRepeatShuttleOrNewServiceMessage
flags to indicate whether the respective message has been sent
Definition: TrainUnit.h:318
TrainFailure
@ TrainFailure
Definition: TrainUnit.h:51
TTrain::MinsDelayed
float MinsDelayed
new at v2.2.0 for operator time to act panel. Calculated in UpdateTrain
Definition: TrainUnit.h:410
TTrack::TSigElement::SigPtr
Graphics::TBitmap * SigPtr
pointer to the graphic
Definition: TrackUnit.h:730
TAllRoutes::FindRouteNumberFromRoute2MultiMapNoErrors
bool FindRouteNumberFromRoute2MultiMapNoErrors(int Caller, int HLoc, int VLoc, int ELink, int &RouteNumber)
If a route is present at H, V & Elink returns true with RouteNumber giving vector position in AllRout...
Definition: TrackUnit.cpp:18094
TTrainController::Last2CharactersBothDigits
bool Last2CharactersBothDigits(int Caller, AnsiString HeadCode)
Checks the last two characters in HeadCode and returns true if both are digits.
Definition: TrainUnit.cpp:10602
TRailGraphics::Code2
Graphics::TBitmap * Code2
Definition: GraphicUnit.h:956
TTrack::GetVectorPositionsFromInactiveTrackMap
TIMPair GetVectorPositionsFromInactiveTrackMap(int Caller, int HLoc, int VLoc, bool &FoundFlag)
Similar to GetVectorPositionFromTrackMap but for inactive elements, a pair is returned because there ...
Definition: TrackUnit.cpp:5749
TTrain::BackgroundColour
TColor BackgroundColour
the background colour of the train's headcode graphics
Definition: TrainUnit.h:465
TTrainOperatingData::EventReported
TActionEventType EventReported
Definition: TrainUnit.h:165
TTrain
Definition: TrainUnit.h:281
TRailGraphics::Code_z
Graphics::TBitmap * Code_z
Definition: GraphicUnit.h:953
TTrainController::CheckTimeValidity
bool CheckTimeValidity(int Caller, AnsiString TimeStr, TDateTime &Time)
returns true if the time complies with requirements
Definition: TrainUnit.cpp:10621
clSignallerStopped
#define clSignallerStopped
Definition: GraphicUnit.h:298
TTrackElement::TrainIDOnBridgeTrackPos23
int TrainIDOnBridgeTrackPos23
Set to the TrainID value when a train is present on the element, bridges can have two trains present ...
Definition: TrackUnit.h:154
TOnePrefDir::GetFixedPrefDirElementAt
const TPrefDirElement & GetFixedPrefDirElementAt(int Caller, int At) const
Return a non-modifiable element at PrefDirVector position 'At'.
Definition: TrackUnit.cpp:11335
FailBuffersPreventingStart
@ FailBuffersPreventingStart
Definition: TrainUnit.h:43
TTrainController::LocServiceTimesArrTimeSort
bool LocServiceTimesArrTimeSort(TLocServiceTimes i, TLocServiceTimes j)
Definition: TrainUnit.h:836
TOneTrainFormattedEntry::Action
AnsiString Action
includes location if relevant
Definition: TrainUnit.h:229
TTrainController::ContinuationTrainExpectationMultiMap
TContinuationTrainExpectationMultiMap ContinuationTrainExpectationMultiMap
Multimap for TContinuationTrainExpectationEntry objects, the access key is the expectation time.
Definition: TrainUnit.h:818
TTrain::StoppedForTrainInFront
bool StoppedForTrainInFront
Definition: TrainUnit.h:442
GapJump
@ GapJump
Definition: TrackUnit.h:67
TRailGraphics::CodeK
Graphics::TBitmap * CodeK
Definition: GraphicUnit.h:974
TTrainController::MissedStops
int MissedStops
Definition: TrainUnit.h:788
NoSequence
@ NoSequence
Definition: TrainUnit.h:75
clCallOnBackground
#define clCallOnBackground
Definition: GraphicUnit.h:292
TTrackElement::Length01
int Length01
Definition: TrackUnit.h:150
TTrackElement::SpeedLimit01
int SpeedLimit01
Definition: TrackUnit.h:150
TTrain::TerminatedMessageSent
bool TerminatedMessageSent
set when a 'train terminated' message has been logged, to prevent its being logged more than once
Definition: TrainUnit.h:377
Finish
@ Finish
Definition: TrainUnit.h:75
TTrain::FinishJoin
void FinishJoin(int Caller)
Carry out the actions needed when a train is waiting to join another train.
Definition: TrainUnit.cpp:6043
TTrainController::FinishedOperation
void FinishedOperation(int Caller)
called when exiting operation mode to delete all trains and timetable data etc
Definition: TrainUnit.cpp:9039
TTrain::MidElement
int MidElement
Definition: TrainUnit.h:337
TTrackElement::TempTrackMarker23
bool TempTrackMarker23
Utility markers for program use.
Definition: TrackUnit.h:140
TAllRoutes::CallonVector
std::vector< TCallonEntry > CallonVector
the store of all call-on entries
Definition: TrackUnit.h:1670
TTrain::LeadElement
int LeadElement
Definition: TrainUnit.h:337
clDerailedBackground
#define clDerailedBackground
Definition: GraphicUnit.h:294
TTrain::RepeatNumber
int RepeatNumber
indicates which of the repeating services this train represents (0 = first service)
Definition: TrainUnit.h:331
TTrainDataEntry::ActionVector
TActionVector ActionVector
all the actions for the train
Definition: TrainUnit.h:204
TTrainController::BaseTime
TDateTime BaseTime
CurrentDateTime (i.e. real time) when operation restarts after a pause.
Definition: TrainUnit.h:651
TTrain::StartSpeed
int StartSpeed
the speed of the train when introduced into the railway (in km/h)
Definition: TrainUnit.h:335
TTrainController::LoadSessionLockedRoutes
void LoadSessionLockedRoutes(int Caller, std::ifstream &SessionFile)
load locked routes from a session file
Definition: TrainUnit.cpp:15058
TTrainController::EarlyPasses
int EarlyPasses
Definition: TrainUnit.h:781
TTrain::HeadCode
AnsiString HeadCode
needs own HeadCode because repeat entries will differ from TrainDataEntry.HeadCode
Definition: TrainUnit.h:300
TDisplay::GetOutputLog7
TLabel * GetOutputLog7()
Definition: DisplayUnit.h:174
TTrainController::TOpTimeToActMultiMapEntry
std::pair< float, THCandTrainPosParam > TOpTimeToActMultiMapEntry
Definition: TrainUnit.h:733
FailMissedSplit
@ FailMissedSplit
Definition: TrainUnit.h:41
TTrain::EntryTime
TDateTime EntryTime
Definition: TrainUnit.h:424
TTrainController::TContinuationAutoSigEntry::FirstDelay
double FirstDelay
Definition: TrainUnit.h:666
TTrainController::SaveTrainDataVectorToFile
void SaveTrainDataVectorToFile(int Caller)
diagnostic function to store all train data to a file for examination, not used normally
Definition: TrainUnit.cpp:14781
TOnePrefDir::PrefDirSize
unsigned int PrefDirSize() const
Return the vector size.
Definition: TrackUnit.h:1386
TTrack::PlotSignal
void PlotSignal(int Caller, TTrackElement TrackElement, TDisplay *Disp)
Plot signals on screen according to their aspect (Attribute value)
Definition: TrackUnit.cpp:5981
End
@ End
Definition: TrackUnit.h:77
TTrain::OneLengthAccelDecel
bool OneLengthAccelDecel
set when a train can only move forwards one element before stopping but needs to accelerate for the f...
Definition: TrainUnit.h:367
TTrain::FloatingLabelNextString
AnsiString FloatingLabelNextString(int Caller, TActionVectorEntry *Ptr)
Used in the floating window to display the 'Next' action.
Definition: TrainUnit.cpp:6879
TTrack::TimetabledLocationNameAllocated
bool TimetabledLocationNameAllocated(int Caller, AnsiString LocationName)
True if a non-empty LocationName found as a timetabled location name i.e. not as a continuation name.
Definition: TrackUnit.cpp:8677
TTrain::FailedTrainNoFinishJoinMessage
bool FailedTrainNoFinishJoinMessage
Definition: TrainUnit.h:312
TTrack::InactiveTrackElementAt
TTrackElement & InactiveTrackElementAt(int Caller, int At)
A range-checked version of InactiveTrackVector.at(At)
Definition: TrackUnit.cpp:10430
ExitRailway
@ ExitRailway
Definition: TrainUnit.h:65
TPrefDirElement::GetTrackVectorPosition
unsigned int GetTrackVectorPosition() const
Returns TrackVectorPosition.
Definition: TrackUnit.h:317
TTrain::StationStopCalculated
bool StationStopCalculated
used in calculating DistanceToStationStop for trains running early before they have reached the stop ...
Definition: TrainUnit.h:373
TTrainController::LoadSessionContinuationAutoSigEntries
void LoadSessionContinuationAutoSigEntries(int Caller, std::ifstream &SessionFile)
load ContinuationAutoSigEntries from a session file
Definition: TrainUnit.cpp:15141
TTrackElement
Basic track elements as implemented in the overall railway layout.
Definition: TrackUnit.h:126
TRailGraphics::Code_n
Graphics::TBitmap * Code_n
Definition: GraphicUnit.h:941
FailMissedNewService
@ FailMissedNewService
Definition: TrainUnit.h:42
TUtilities::Format96HHMMSS
AnsiString Format96HHMMSS(TDateTime DateTime)
formats a TDateTime into an AnsiString of the form hh:mm:ss where hh runs from 00 to 95 & resets when...
Definition: Utilities.cpp:581
TRailGraphics::smSolidBgnd
Graphics::TBitmap * smSolidBgnd
Definition: GraphicUnit.h:991
clTRSBackground
#define clTRSBackground
Definition: GraphicUnit.h:303
TTrainController::OtherMissedEvents
int OtherMissedEvents
Definition: TrainUnit.h:793
TTrainDataEntry::Description
AnsiString Description
headcode is the first train's headcode, rest are calculated from repeat information; ServiceReference...
Definition: TrainUnit.h:188
TTrainController::EarlyExits
int EarlyExits
Definition: TrainUnit.h:782
TTrain::ExitSpeedHalf
double ExitSpeedHalf
speed when half way into the next element
Definition: TrainUnit.h:389
SignalPost
@ SignalPost
Definition: TrackUnit.h:67
TRailGraphics::Code_h
Graphics::TBitmap * Code_h
Definition: GraphicUnit.h:935
TTrain::LastActionTime
TDateTime LastActionTime
time of the last timetabled action, used to ensure at least a 30 second delay before the next action
Definition: TrainUnit.h:428
TDisplay::GetOutputLog1
TLabel * GetOutputLog1()
Return pointers to warning message logs (appear above the railway display during operation)
Definition: DisplayUnit.h:143
TAllRoutes::TRoute2MultiMapIterator
TRoute2MultiMap::iterator TRoute2MultiMapIterator
Definition: TrackUnit.h:1647
TTrain::TrainFailurePending
bool TrainFailurePending
set when failure due & takes effect when all PlotElements properly set, added at v2....
Definition: TrainUnit.h:320
FailMissedTerminate
@ FailMissedTerminate
Definition: TrainUnit.h:42
TTrainController::SecondPassActions
bool SecondPassActions(int Caller, bool GiveMessages, bool &TwoLocationFlag)
Carry out further detailed timetable consistency checks, return true for success.
Definition: TrainUnit.cpp:11665
TRailGraphics::Code9
Graphics::TBitmap * Code9
Definition: GraphicUnit.h:963
TTrainDataEntry::HeadCode
AnsiString HeadCode
Definition: TrainUnit.h:188
TTrain::TrainHasFailed
void TrainHasFailed(int Caller)
Called when there is a random train failure.
Definition: TrainUnit.cpp:5371
clNormalBackground
#define clNormalBackground
Definition: GraphicUnit.h:297
TActionVectorEntry::NonRepeatingShuttleLinkEntryPtr
TTrainDataEntry * NonRepeatingShuttleLinkEntryPtr
pointer used by shuttles for the non-shuttle train links, in & out, the corresponding non-shuttle lin...
Definition: TrainUnit.h:129
TTrainController::SPADRisks
int SPADRisks
Definition: TrainUnit.h:795
TTrainController::TLocServiceTimesVector
std::vector< TLocServiceTimes > TLocServiceTimesVector
Definition: TrainUnit.h:719
TTrainController::LocServiceTimesAtLocTimeSort
bool LocServiceTimesAtLocTimeSort(TLocServiceTimes i, TLocServiceTimes j)
Definition: TrainUnit.h:845
TTrain::EntrySpeed
double EntrySpeed
speed at which the train enters the next element
Definition: TrainUnit.h:387
TConfiguration
TConfiguration
< describes the type of track link. 'End' is used for both buffer stop and continuation entry/exit po...
Definition: TrackUnit.h:76
TTrain::TrainGone
bool TrainGone
set when train has left the railway, so it can be removed from the display at the next clock tick
Definition: TrainUnit.h:438
TTrackType
TTrackType
< describes the type of track element
Definition: TrackUnit.h:66
TTrainController::OnTimePasses
int OnTimePasses
Definition: TrainUnit.h:791
TTrainController::SigSHigh
bool SigSHigh
Definition: TrainUnit.h:756
TRailGraphics::smCaramel
Graphics::TBitmap * smCaramel
Definition: GraphicUnit.h:879
TGraphicElement::PlotOriginal
void PlotOriginal(int Caller, TDisplay *Disp)
Plot the original graphic on screen.
Definition: TrackUnit.cpp:1858
clBufferAttentionNeeded
#define clBufferAttentionNeeded
Definition: GraphicUnit.h:290
TTrain::StoppedAfterSPAD
bool StoppedAfterSPAD
Definition: TrainUnit.h:442
FailSPAD
@ FailSPAD
Definition: TrainUnit.h:40
TTrainController::OnTimeArrivals
int OnTimeArrivals
Definition: TrainUnit.h:789
clTrainFailedBackground
#define clTrainFailedBackground
Definition: GraphicUnit.h:304
TRailGraphics::Code_x
Graphics::TBitmap * Code_x
Definition: GraphicUnit.h:951
clFrontCodeSignaller
#define clFrontCodeSignaller
Definition: GraphicUnit.h:295
TimeCmdHeadCode
@ TimeCmdHeadCode
Definition: TrainUnit.h:64
Utilities
TUtilities * Utilities
Definition: Utilities.cpp:47
TFixedTrackPiece::SmallGraphicPtr
Graphics::TBitmap * SmallGraphicPtr
the track bitmap for display on the zoomed-out railway
Definition: TrackUnit.h:95
TActionVectorEntry::ArrivalTime
TDateTime ArrivalTime
Definition: TrainUnit.h:115
TTrain::StoppedAtBuffers
bool StoppedAtBuffers
Definition: TrainUnit.h:442
TTrainController::CreateTTAnalysisFile
bool CreateTTAnalysisFile(int Caller, AnsiString RailwayTitle, AnsiString TimetableTitle, AnsiString CurDir, bool ArrChecked, bool DepChecked, bool AtLocChecked, bool DirChecked, int ArrRange, int DepRange)
Generate a timetable analysis file in the 'Formatted Timetables' folder, return false if failed for a...
Definition: TrainUnit.cpp:15938
TRailGraphics::Code_b
Graphics::TBitmap * Code_b
Definition: GraphicUnit.h:929
TTrain::Mass
int Mass
in kg
Definition: TrainUnit.h:418
TRailGraphics::Code0
Graphics::TBitmap * Code0
Definition: GraphicUnit.h:954
TTrainController::ProcessOneTimetableLine
bool ProcessOneTimetableLine(int Caller, int Count, AnsiString OneLine, bool &EndOfFile, bool FinalCall, bool GiveMessages, bool CheckLocationsExistInRailway)
Carry out preliminary (mainly syntax) validity checks on a single timetable service entry and (if Fin...
Definition: TrainUnit.cpp:9939
TTrainController::TContinuationTrainExpectationEntry::IncrementalMinutes
int IncrementalMinutes
Repeat separation in minutes.
Definition: TrainUnit.h:690
TTrain::LeadExitPos
int LeadExitPos
Definition: TrainUnit.h:337
TTrain::FrontElementLength
int FrontElementLength
values associated with the element immediately in front of the train (speed in km/h,...
Definition: TrainUnit.h:416
TTimetableShuttleLinkType
TTimetableShuttleLinkType
Definition: TrainUnit.h:79
TRailGraphics::CodeP
Graphics::TBitmap * CodeP
Definition: GraphicUnit.h:979
Pass
@ Pass
Definition: TrainUnit.h:52
TNumList
std::list< int > TNumList
a list of valid train exit TrackVector positions for 'Fer' entries
Definition: TrainUnit.h:91
TTrainController::DerailWarning
bool DerailWarning
Definition: TrainUnit.h:742
TTrainController::TContinuationTrainExpectationMultiMapPair
std::pair< TDateTime, TContinuationTrainExpectationEntry > TContinuationTrainExpectationMultiMapPair
a single multimap entry
Definition: TrainUnit.h:704
RouteForceCancelled
@ RouteForceCancelled
Definition: TrainUnit.h:44
EnRoute
@ EnRoute
Definition: TrainUnit.h:70
TTrain::IsTrainIDOnBridgeTrackPos23
bool IsTrainIDOnBridgeTrackPos23(int Caller, unsigned int TrackVectorPosition)
True if train is on a bridge on trackpos 2 & 3.
Definition: TrainUnit.cpp:2993
TTrack::DiagonalFouledByTrain
bool DiagonalFouledByTrain(int Caller, int HLoc, int VLoc, int DiagonalLinkNumber, int &TrainID)
As DiagonalFouledByRouteOrTrain (in TAllRoutes) but only checks for a train (may or may not be a rout...
Definition: TrackUnit.cpp:11153
TTrainController::TotLateDepMins
float TotLateDepMins
Definition: TrainUnit.h:773
TDisplay::ZoomOutFlag
bool ZoomOutFlag
true when zoomed-out
Definition: DisplayUnit.h:69
TRailGraphics::CodeC
Graphics::TBitmap * CodeC
Definition: GraphicUnit.h:966
TTrain::PlotElement
int PlotElement[4]
the TrackVectorPosition of the element where each of the 4 headcode characters is plotted (need to be...
Definition: TrainUnit.h:449
TTrainController::TServiceCallingLocsList
std::list< AnsiString > TServiceCallingLocsList
Used in determining train directions in timetable conflict analysis.
Definition: TrainUnit.h:722
TTrainController::TrainVector
TTrainVector TrainVector
vector containing all trains currently in the railway
Definition: TrainUnit.h:826
TTrack::ResetAllTrainIDElements
void ResetAllTrainIDElements(int Caller)
Track elements have members that indicates whether and on what track a train is present (TrainIDOnEle...
Definition: TrackUnit.cpp:7509
TRailGraphics::Code_l
Graphics::TBitmap * Code_l
Definition: GraphicUnit.h:939
TTrainController::TContinuationAutoSigEntry
< TTClockTime when last session saved - to prevent display of warning message on exit session if < 5 ...
Definition: TrainUnit.h:664
TUtilities::CallLogPop
void CallLogPop(int Caller)
pops the last entry off the call stack, throws an error if called when empty
Definition: Utilities.cpp:50
TTrain::IncrementalMinutes
int IncrementalMinutes
the number of minutes to increment by in repeat entries
Definition: TrainUnit.h:325
TTrain::MaximumSpeedLimit
static const int MaximumSpeedLimit
km/h
Definition: TrainUnit.h:294
TRailGraphics::CodeI
Graphics::TBitmap * CodeI
Definition: GraphicUnit.h:972
TTrainController::TotEarlyArrMins
float TotEarlyArrMins
values for performance file summary
Definition: TrainUnit.h:768
TTrain::WriteTrainToImage
void WriteTrainToImage(int Caller, Graphics::TBitmap *Bitmap)
Called by TTrainController::WriteTrainsToImage (called by TInterface::SaveOperatingImage1Click) to ad...
Definition: TrainUnit.cpp:8359
TRailGraphics::CodeL
Graphics::TBitmap * CodeL
Definition: GraphicUnit.h:975
TTrainDataEntry::MaxBrakeRate
double MaxBrakeRate
in metres/sec/sec
Definition: TrainUnit.h:190
FNSNonRepeatToShuttle
@ FNSNonRepeatToShuttle
Definition: TrainUnit.h:64
TDisplay::PlotOutput
void PlotOutput(int Caller, int HPos, int VPos, Graphics::TBitmap *PlotItem)
Plot the graphic at screen position HPos & VPos.
Definition: DisplayUnit.cpp:85
TUtilities::SaveFileBool
void SaveFileBool(std::ofstream &OutFile, bool SaveBool)
stores '1' if the bool is true or '0' if false to the file, then a CR
Definition: Utilities.cpp:108
TTrainDataEntry::SignallerSpeed
int SignallerSpeed
in km/h for use when under signaller control
Definition: TrainUnit.h:200
TTrain::AbleToMove
bool AbleToMove(int Caller)
Indicates that a train is not prevented from moving - used to allow appropriate popup menu options wh...
Definition: TrainUnit.cpp:6735
TTrainController::MTBFHours
double MTBFHours
Mean time between failures in timetable clock hours.
Definition: TrainUnit.h:759
TOneRoute::ForceCancelRoute
void ForceCancelRoute(int Caller)
Cancel a route immediately if a train occupies it when travelling in the wrong direction (or occupies...
Definition: TrackUnit.cpp:17270
TTrain::TrainMode
TTrainMode TrainMode
mode of operation - either Timetable (running under timetable control) or Signaller (running under si...
Definition: TrainUnit.h:430
TTrainController::CheckNonRepeatingShuttleLinkTime
bool CheckNonRepeatingShuttleLinkTime(int Caller, TDateTime ReverseEventTime, TDateTime ForwardEventTime, int RepeatMins, int RepeatNumber)
The forward train is the finish shuttle entry 'Fns-sh', the reverse (new non-repeating service) time ...
Definition: TrainUnit.cpp:14450
TTrack::ThisNamedLocationLongEnoughForSplit
bool ThisNamedLocationLongEnoughForSplit(int Caller, AnsiString LocationName, int FirstNamedElementPos, int &SecondNamedElementPos, int &FirstNamedLinkedElementPos, int &SecondNamedLinkedElementPos)
See above under 'OneNamedLocationLongEnoughForSplit'.
Definition: TrackUnit.cpp:10571
TUtilities::Format96HHMM
AnsiString Format96HHMM(TDateTime DateTime)
formats a TDateTime into an AnsiString of the form hh:mm where hh runs from 00 to 95 & resets when it...
Definition: Utilities.cpp:600
TTrain::CallOnMaxSpeed
static const int CallOnMaxSpeed
km/h
Definition: TrainUnit.h:288
TTrain::CheckAndCancelRouteForWrongEndEntry
void CheckAndCancelRouteForWrongEndEntry(int Caller, int Element, int EntryPos)
Checks whether Element and EntryPos (where train is about to enter) is on an existing route (or cross...
Definition: TrainUnit.cpp:3201
TRailGraphics::CodeW
Graphics::TBitmap * CodeW
Definition: GraphicUnit.h:986
TTrain::ReleaseTime
TDateTime ReleaseTime
Definition: TrainUnit.h:426
TTrain::GetTrainHeadCode
AnsiString GetTrainHeadCode(int Caller)
Returns the train headcode, taking account of the RepeatNumber.
Definition: TrainUnit.cpp:4961
TTrain::LowEntryValue
bool LowEntryValue(int EntryLink) const
Returns true if EntryLink is 1, 2, 4 or 7, in these circumstances the front of the train (i....
Definition: TrainUnit.cpp:2514
RearSplit
@ RearSplit
Definition: TrainUnit.h:50
TActionVectorEntry::FrontStartOrRepeatDigits
int FrontStartOrRepeatDigits
dual-purpose variables used for the TrackVectorPositions of the rear and front train starting element...
Definition: TrainUnit.h:113
SignallerControlStop
@ SignallerControlStop
Definition: TrainUnit.h:52
TTrack::TrackVector
TTrackVector TrackVector
Definition: TrackUnit.h:815
TTrain::HasTrainGone
bool HasTrainGone()
Check whether the train has left the railway, so that it can be removed from the display at the next ...
Definition: TrainUnit.h:614
TRailGraphics::Code_i
Graphics::TBitmap * Code_i
Definition: GraphicUnit.h:936
TTrainController::ExcessLCDownMins
float ExcessLCDownMins
total excess time in minutes over the 3 minutes barriers down allowance for level crossings
Definition: TrainUnit.h:766
TRailGraphics::Code5
Graphics::TBitmap * Code5
Definition: GraphicUnit.h:959
TTrackElement::CallingOnSet
bool CallingOnSet
Used for for signals only when a train is being called on - used to plot the position lights.
Definition: TrackUnit.h:136
TUtilities::SaveFileInt
void SaveFileInt(std::ofstream &OutFile, int SaveInt)
stores the int value to the file, then a CR
Definition: Utilities.cpp:121
TrainController
TTrainController * TrainController
the object pointer, one object only - created in InterfaceUnit
Definition: TrainUnit.cpp:54
TTrack::OneNamedLocationLongEnoughForSplit
bool OneNamedLocationLongEnoughForSplit(int Caller, AnsiString LocationName)
Definition: TrackUnit.cpp:10465
TTrain::BufferAtExit
bool BufferAtExit(int Caller, int Element, int Exitpos) const
True if Element is a buffer and Exitpos is the buffer end.
Definition: TrainUnit.cpp:2925
TTrain::IsTrainTerminating
bool IsTrainTerminating(int Caller)
True if train service terminates at its current location.
Definition: TrainUnit.cpp:6707
TTrainController::SecondPassMessage
void SecondPassMessage(bool GiveMessages, AnsiString Message)
Give a user message during timetable integrity checking if GiveMessages is true, ignore if false.
Definition: TrainUnit.cpp:14548
SignallerStepForward
@ SignallerStepForward
Definition: TrainUnit.h:52
TTrack::GetHLocMin
int GetHLocMin()
Definition: TrackUnit.h:881
TTrainController::CheckCrossReferencesAndSetData
bool CheckCrossReferencesAndSetData(int Caller, AnsiString SoughtHeadCode, AnsiString SeekingHeadCode, bool Shuttle, bool GiveMessages)
A timetable validation function where all service cross references are checked for validity and set p...
Definition: TrainUnit.cpp:13216
TDisplay::GetOutputLog5
TLabel * GetOutputLog5()
Definition: DisplayUnit.h:164
TTrainController::LateExits
int LateExits
Definition: TrainUnit.h:787
TTrainController::OpTimeToActUpdateCounter
unsigned int OpTimeToActUpdateCounter
<List of all ServiceRefs that have two or more same locations without a cdt between - loaded during S...
Definition: TrainUnit.h:810
TTrain::BeingCalledOn
bool BeingCalledOn
in course of being called on to a station
Definition: TrainUnit.h:351
TUtilities::CheckFileInt
bool CheckFileInt(std::ifstream &InFile, int Lowest, int Highest)
checks that the value is an int lying between Lowest & Highest (inclusive), returns true for success
Definition: Utilities.cpp:238
TRailGraphics::ChangeForegroundColour2
void ChangeForegroundColour2(int Caller, Graphics::TBitmap *BitmapIn, Graphics::TBitmap *BitmapOut, TColor NewForegroundColour, TColor BackgroundColour)
New function to do the same as the above but with fewer pixel changes - for use in LoadSession to avo...
Definition: GraphicUnit.cpp:3388
TRailGraphics::Code_e
Graphics::TBitmap * Code_e
Definition: GraphicUnit.h:932
TTrain::HoldAtLocationInTTMode
bool HoldAtLocationInTTMode
true if actions are needed before train departs
Definition: TrainUnit.h:302
TTrain::PlotTrainInZoomOutMode
void PlotTrainInZoomOutMode(int Caller, bool Flash)
Plots the train on screen in zoomed-out mode, state of 'Flash' determines whether the flashing trains...
Definition: TrainUnit.cpp:8169
TTrain::LoadOneSessionTrain
void LoadOneSessionTrain(int Caller, std::ifstream &InFile)
Create one train with relevant member values from the sesion file.
Definition: TrainUnit.cpp:7448
TTrain::PlotBackgroundGraphic
void PlotBackgroundGraphic(int Caller, int ArrayNumber, TDisplay *Disp) const
Replot the graphic pointed to by BackgroundPtr (see above) after a train has passed.
Definition: TrainUnit.cpp:2917
TTrainController::TotLateArrMins
float TotLateArrMins
Definition: TrainUnit.h:772
TTrain::HOffset
int HOffset[4]
Definition: TrainUnit.h:445
FailMissedArrival
@ FailMissedArrival
Definition: TrainUnit.h:41
TTrain::UpdateCounter
unsigned int UpdateCounter
used in train splitting operations to prevent too frequent checks for a location being long enough fo...
Definition: TrainUnit.h:422
NewService
@ NewService
Definition: TrainUnit.h:50
FailEnterLockedRoute
@ FailEnterLockedRoute
Definition: TrainUnit.h:42
TTrainController::PlotAllTrainsInZoomOutMode
void PlotAllTrainsInZoomOutMode(int Caller, bool Flash)
Plots all trains on screen in zoomed-out mode, state of 'Flash' determines whether the flashing train...
Definition: TrainUnit.cpp:15294
TOneRoute
A descendent of TOnePrefDir used for routes. Used during contruction of a route (ConstructRoute) and ...
Definition: TrackUnit.h:1486
TTrain::SignallerStopped
bool SignallerStopped
Definition: TrainUnit.h:442
TTrain::BufferZoomOutFlashRequired
bool BufferZoomOutFlashRequired
set when train is at buffers and is to flash in zoomout mode (i.e. when reaches buffers unexpectedly ...
Definition: TrainUnit.h:353
TAllRoutes::TRouteType
TRouteType
Definition: TrackUnit.h:1630
TTrain::TrainDataEntryPtr
TTrainDataEntry * TrainDataEntryPtr
points to the current position in the timetable's TrainDataVector
Definition: TrainUnit.h:343
TTrainDataEntry::StartSpeed
int StartSpeed
in km/h
Definition: TrainUnit.h:202
TTrainDataEntry::NumberOfTrains
int NumberOfTrains
number of repeats + 1
Definition: TrainUnit.h:198
TTrainController::Derailments
int Derailments
Definition: TrainUnit.h:779
clStationStopBackground
#define clStationStopBackground
Definition: GraphicUnit.h:301
TDisplay::GetOutputLog6
TLabel * GetOutputLog6()
Definition: DisplayUnit.h:169
ShuttleLink
@ ShuttleLink
Definition: TrainUnit.h:80
TRailGraphics::CodeM
Graphics::TBitmap * CodeM
Definition: GraphicUnit.h:976
Crossover
@ Crossover
Definition: TrackUnit.h:67
TTrain::ClearToNextSignal
bool ClearToNextSignal(int Caller)
Checks forward from train LeadElement, following leading point attributes but ignoring trailing point...
Definition: TrainUnit.cpp:4569
TTrainController::TContinuationTrainExpectationEntry::Description
AnsiString Description
service description
Definition: TrainUnit.h:684
AtLocation
@ AtLocation
Definition: TrainUnit.h:70
Signal
@ Signal
Definition: TrackUnit.h:77
TRailGraphics::Code_k
Graphics::TBitmap * Code_k
Definition: GraphicUnit.h:938
TRailGraphics::CodeF
Graphics::TBitmap * CodeF
Definition: GraphicUnit.h:969
TTrack::ContinuationNameMap
std::map< AnsiString, char > ContinuationNameMap
map of all continuation names, char is a dummy
Definition: TrackUnit.h:787
TTrackElement::TrainIDOnBridgeTrackPos01
int TrainIDOnBridgeTrackPos01
Definition: TrackUnit.h:154
TAllRoutes::GetRouteTypeAndNumber
TRouteType GetRouteTypeAndNumber(int Caller, int TrackVectorPosition, int LinkPos, int &RouteNumber)
Examines Route2MultiMap and if the element at TrackVectorPosition with LinkPos (can be entry or exit)...
Definition: TrackUnit.cpp:17787
TTrainController::TrainDataVector
TTrainDataVector TrainDataVector
vector containing the internal timetable
Definition: TrainUnit.h:824
TTrainController::ConsolidateSARNTAtLoc
AnsiString ConsolidateSARNTAtLoc(int Caller, const AnsiString Input, int &NumTrainsAtLoc)
Removes duplicates from and sorts ServiceAndRepeatNumTotal into alphabetical order for AtLoc listing ...
Definition: TrainUnit.cpp:18316
TAllRoutes::NotAutoSigsRoute
@ NotAutoSigsRoute
Definition: TrackUnit.h:1631
TTrain::ExitTimeHalf
TDateTime ExitTimeHalf
Definition: TrainUnit.h:424
Exited
@ Exited
Definition: TrainUnit.h:86
TTrack::TrackElementAt
TTrackElement & TrackElementAt(int Caller, int At)
A range-checked version of TrackVector.at(At)
Definition: TrackUnit.cpp:10416
TTrainController::LoadSessionTrains
void LoadSessionTrains(int Caller, std::ifstream &SessionFile)
load trains from a session file
Definition: TrainUnit.cpp:14992
TTrain::CheckOneSessionTrain
static bool CheckOneSessionTrain(std::ifstream &InFile)
Carries out an integrity check for the train section of a session file, if fails a message is given a...
Definition: TrainUnit.cpp:7735
TAllRoutes::GetRouteTypeAndGraphics
TRouteType GetRouteTypeAndGraphics(int Caller, int TrackVectorPosition, int LinkPos, Graphics::TBitmap *&EXGraphicPtr, Graphics::TBitmap *&EntryDirectionGraphicPtr)
Examines Route2MultiMap for the element at TrackVectorPosition with LinkPos (can be entry or exit).
Definition: TrackUnit.cpp:17613
TRailGraphics::CodeY
Graphics::TBitmap * CodeY
Definition: GraphicUnit.h:988
TTrain::SetOneGraphicCode
Graphics::TBitmap * SetOneGraphicCode(char CodeChar)
Return a pointer to the graphic corresponding to the character 'CodeVhar'.
Definition: TrainUnit.cpp:2131
TTrain::DerailPending
bool DerailPending
Definition: TrainUnit.h:442
TTrainMode
TTrainMode
indicates train operating mode, 'None' for not in use
Definition: TrainUnit.h:57
TAllRoutes::TCallonEntry
Used to store relevant values when a call-on found, ready for plotting an unrestricted route.
Definition: TrackUnit.h:1652
TRailGraphics::Code_a
Graphics::TBitmap * Code_a
Definition: GraphicUnit.h:928
TTrain::StoppedAtLocation
bool StoppedAtLocation
Definition: TrainUnit.h:442
TTrainController::TContinuationTrainExpectationMultiMapIterator
TContinuationTrainExpectationMultiMap::iterator TContinuationTrainExpectationMultiMapIterator
iterator for the multimap
Definition: TrainUnit.h:702
TTrain::MaximumMassLimit
static const int MaximumMassLimit
kg (i.e. 10,000 tonnes)
Definition: TrainUnit.h:290
TTrainController::StopTTClockFlag
bool StopTTClockFlag
when true the timetable clock is stopped, used for messages display and train popup menu display etc
Definition: TrainUnit.h:744
FrontSplit
@ FrontSplit
Definition: TrainUnit.h:50
TTrain::MaxExitSpeed
double MaxExitSpeed
the maximum speed that the train can exit the next element
Definition: TrainUnit.h:397
TTrainController::BFHigh
bool BFHigh
Definition: TrainUnit.h:756
TRailGraphics::Code4
Graphics::TBitmap * Code4
Definition: GraphicUnit.h:958
TRailGraphics::Code_g
Graphics::TBitmap * Code_g
Definition: GraphicUnit.h:934
TTrain::LagEntryPos
int LagEntryPos
Definition: TrainUnit.h:337
SNTShuttle
@ SNTShuttle
Definition: TrainUnit.h:64
TRailGraphics::CodeG
Graphics::TBitmap * CodeG
Definition: GraphicUnit.h:970
Leave
@ Leave
Definition: TrainUnit.h:50
TTrainController::UnexpectedExits
int UnexpectedExits
Definition: TrainUnit.h:797
TTrainController::TLocServiceTimes::AtLocTime
AnsiString AtLocTime
Definition: TrainUnit.h:714
TTrainController::ServiceReference
AnsiString ServiceReference
String used to display the offending service in timetable error messages.
Definition: TrainUnit.h:740
TTrain::FirstLaterStopRecoverableTime
float FirstLaterStopRecoverableTime
this used to deduct from RecoverableTime when arrive at a location for OperatorActionpanel
Definition: TrainUnit.h:414
TTrain::LeavingUnderSigControlAtContinuation
bool LeavingUnderSigControlAtContinuation
set when the train has reached an exit continuation when under signaller control, used to prevent the...
Definition: TrainUnit.h:365
TTrain::PlotTrainGraphic
void PlotTrainGraphic(int Caller, int ArrayNumber, TDisplay *Disp)
Plot the train's headcode character corresponding to ArrayNumber.
Definition: TrainUnit.cpp:2898
TRailGraphics::Code_j
Graphics::TBitmap * Code_j
Definition: GraphicUnit.h:937
NoFormat
@ NoFormat
Definition: TrainUnit.h:64
TTrainController::TwoOrMoreLocationsWarningGiven
bool TwoOrMoreLocationsWarningGiven
new at v2.6.0 to allow loops
Definition: TrainUnit.h:752
FailBufferCrash
@ FailBufferCrash
Definition: TrainUnit.h:43
WaitingForFJO
@ WaitingForFJO
Definition: TrainUnit.h:43
FailMissedExitRailway
@ FailMissedExitRailway
Definition: TrainUnit.h:42
TimeTimeLoc
@ TimeTimeLoc
Definition: TrainUnit.h:64
TTrain::GetOffsetValues
void GetOffsetValues(int Caller, int &HOffset, int &VOffset, int Link) const
Sets HOffset & VOffset (see above) for a single headcode character depending on the Link value.
Definition: TrainUnit.cpp:2443
TTrain::Derailed
bool Derailed
Definition: TrainUnit.h:442
TRailGraphics::ChangeSpecificColour
void ChangeSpecificColour(int Caller, Graphics::TBitmap *BitmapIn, Graphics::TBitmap *BitmapOut, TColor ColourToBeChanged, TColor NewColour)
Definition: GraphicUnit.cpp:3417
Enter
@ Enter
Definition: TrainUnit.h:50
TRailGraphics::bm93set
Graphics::TBitmap * bm93set
Definition: GraphicUnit.h:514
TTrack::GetTrackVectorPositionFromString
int GetTrackVectorPositionFromString(int Caller, AnsiString String, bool GiveMessages)
Takes the ElementID value (an AnsiString) (e.g. "8-13", "N43-N127", etc) and returns the correspondin...
Definition: TrackUnit.cpp:7724
TRailGraphics::Code_q
Graphics::TBitmap * Code_q
Definition: GraphicUnit.h:944
TTrain::NewTrainService
void NewTrainService(int Caller)
Carry out the actions needed when a train forms a new service (code Fns)
Definition: TrainUnit.cpp:6237
TTrainController::SaveSessionLockedRoutes
void SaveSessionLockedRoutes(int Caller, std::ofstream &SessionFile)
save locked routes to a session file
Definition: TrainUnit.cpp:15041
TTrain::OriginalPowerAtRail
double OriginalPowerAtRail
new at v2.4.0 to store value before a failure so it can be restored from here when repaired
Definition: TrainUnit.h:408
TTrainFormattedInformation
Contains all information for a single timetable entry for use in the formatted timetable.
Definition: TrainUnit.h:255
TTrainController::IsSNTEntryLocated
bool IsSNTEntryLocated(int Caller, const TTrainDataEntry &TDEntry, AnsiString &LocationName)
New trains introduced with 'Snt' may be at a timetabled location or elsewhere. This function checks a...
Definition: TrainUnit.cpp:13746
TTrainController::TContinuationTrainExpectationEntry::VectorPosition
int VectorPosition
TrackVectorPosition for the continuation element.
Definition: TrainUnit.h:694
TTrainController::TTrainController
TTrainController()
Constructor.
Definition: TrainUnit.cpp:8688
Timetable
@ Timetable
Definition: TrainUnit.h:58
TUtilities::SaveFileDouble
void SaveFileDouble(std::ofstream &OutFile, double SaveDouble)
converts the double value to a string (if double stored directly it is truncated to 6 digits) then st...
Definition: Utilities.cpp:127
TTrainController::GetRepeatHeadCode
AnsiString GetRepeatHeadCode(int Caller, AnsiString BaseHeadCode, int RepeatNumber, int IncDigits)
Return the service headcode for the repeat service.
Definition: TrainUnit.cpp:14119
TTrain::AbleToMoveButForSignal
bool AbleToMoveButForSignal(int Caller)
Indicates that a train is only prevented from moving by a signal - used to allow appropriate popup me...
Definition: TrainUnit.cpp:6790
TTrainController::OpActionPanelVisible
bool OpActionPanelVisible
new v2.2.0 flag to prevent time to act functions when not visible
Definition: TrainUnit.h:750
TTrack::GetAnyElementOppositeLinkPos
int GetAnyElementOppositeLinkPos(int Caller, int TrackVectorPosition, int LinkPos, bool &Derail)
Return the opposite link position for the element at TrackVectorPosition with link position LinkPos,...
Definition: TrackUnit.cpp:11004
TTrack::GapFlashRedPosition
int GapFlashRedPosition
TrackVectorPosition of the gap element that is flashing green or red.
Definition: TrackUnit.h:778
TActionVectorEntry::NumberOfRepeats
int NumberOfRepeats
the number of repeating services
Definition: TrainUnit.h:111
TAllRoutes::GetFixedRouteAt
const TOneRoute & GetFixedRouteAt(int Caller, int At) const
Returns a constant reference to the route at AllRoutesVector position 'At', after performing range ch...
Definition: TrackUnit.cpp:17456
TUtilities::clTransparent
TColor clTransparent
the display background colour, can be white, black or dark blue
Definition: Utilities.h:65
TTrainController::SignalStopWarning
bool SignalStopWarning
Definition: TrainUnit.h:742
TPrefDirElement::GetELinkPos
int GetELinkPos() const
Returns the ELink array position.
Definition: TrackUnit.h:287
TTrainController::CalcOperatingAndNotStartedTrainLateness
void CalcOperatingAndNotStartedTrainLateness(int Caller)
calculates additional lateness values for trains that haven't reached their destinations yet
Definition: TrainUnit.cpp:19249
TDisplay::PerformanceLog
void PerformanceLog(int Caller, AnsiString Statement)
Send Statement to the performance log on screen and to the file.
Definition: DisplayUnit.cpp:521
ShuttleLinkTypeForRepeatEntry
@ ShuttleLinkTypeForRepeatEntry
Definition: TrainUnit.h:80
TTrain::SignallerStopBrakeRate
double SignallerStopBrakeRate
the train brake rate when stopping under signaller control
Definition: TrainUnit.h:403
TTrainController::TLocServiceTimes::Location
AnsiString Location
Definition: TrainUnit.h:712
TOneCompleteFormattedTrain
A single train with its headcode + list of actions for use in the formatted timetable.
Definition: TrainUnit.h:242
TTrainController::SignallerTrainRemovedOnAutoSigsRoute
bool SignallerTrainRemovedOnAutoSigsRoute
true if train was on an AutoSigsRoute when removed by the signaller
Definition: TrainUnit.h:748
Terminate
@ Terminate
Definition: TrainUnit.h:50
TTrainController::MRSLow
bool MRSLow
Definition: TrainUnit.h:756
TTrack::ActiveTrackElementNameMap
TActiveTrackElementNameMap ActiveTrackElementNameMap
map of active track element names
Definition: TrackUnit.h:790
TTrain::Crashed
bool Crashed
Definition: TrainUnit.h:442
TTrackElement::HLoc
int HLoc
Definition: TrackUnit.h:148
TTrain::HeadCodePosition
Graphics::TBitmap * HeadCodePosition[4]
Set from the HeadCodeGrPtr[4] pointer values, HeadCodePosition[0] is always the front,...
Definition: TrainUnit.h:456
TTrain::TrainID
int TrainID
the train's identification number
Definition: TrainUnit.h:340
Running
@ Running
Definition: TrainUnit.h:86
TAllFormattedTrains
std::vector< TTrainFormattedInformation > TAllFormattedTrains
vector of all timetabled trains for use in the formatted timetable
Definition: TrainUnit.h:264
TRailGraphics::ChangeBackgroundColour3
void ChangeBackgroundColour3(int Caller, Graphics::TBitmap *BitmapIn, Graphics::TBitmap *BitmapOut, TColor NewBackgroundColour, TColor OldBackgroundColour)
as above but uses Scanline
Definition: GraphicUnit.cpp:3514
TRailGraphics::smCyan
Graphics::TBitmap * smCyan
Definition: GraphicUnit.h:880
TTrainController::MovingSuccessor
bool MovingSuccessor(const TActionVectorEntry &AVEntry)
A shorthand function that returns true if the successor to a given timetable action command should be...
Definition: TrainUnit.cpp:13088
TTrainController::LogActionError
void LogActionError(int Caller, AnsiString HeadCode, AnsiString OtherHeadCode, TActionEventType ActionEventType, AnsiString LocationID)
Send an error message to the performance log and file, and as a warning if appropriate.
Definition: TrainUnit.cpp:14560
TTrainController::CalcDistanceToRedSignalandStopTime
int CalcDistanceToRedSignalandStopTime(int Caller, int TrackVectorPosition, int TrackVectorPositionEntryPos, bool SigControlAndCanPassRedSignal, TActionVectorEntry *AVPtr, AnsiString HeadCode, int TrainID, float &CurrentStopTime, float &LaterStopTime, float &RecoverableTime, int &AvTrackSpeed)
new v2.2.0, calcs distance to red signal, returns -1 for no signal found, for autosigs route after ne...
Definition: TrainUnit.cpp:19517
TTrainController::TrainVectorAt
TTrain & TrainVectorAt(int Caller, int VecPos)
Return a reference to the train at position VecPos in the TrainVector, carries out range checking on ...
Definition: TrainUnit.cpp:15317
TTrain::FrontElementSpeedLimit
int FrontElementSpeedLimit
Definition: TrainUnit.h:416
TTrack::NumberOfPlatforms
int NumberOfPlatforms(int Caller, AnsiString LocationName)
Returns the number of separate platforms (not platform elements) at a given location,...
Definition: TrackUnit.cpp:11224
TTrack::TSigElement::SpeedTag
int SpeedTag
the TrackElement SpeedTag value - specifies the signal element
Definition: TrackUnit.h:726
TTrain::DeleteTrain
void DeleteTrain(int Caller)
This is a housekeeping function to delete train heap objects (bitmaps) explicitly rather than by usin...
Definition: TrainUnit.cpp:215
TTrainController::NumFailures
int NumFailures
Definition: TrainUnit.h:798
TTrain::ExitSpeedFull
double ExitSpeedFull
speed when leaving the next element
Definition: TrainUnit.h:391
TTrain::SetHeadCodeGraphics
void SetHeadCodeGraphics(int Caller, AnsiString Code)
Set the four HeadCodeGrPtr[4] pointers to the appropriate character graphics with the current backgro...
Definition: TrainUnit.cpp:2328
TAllRoutes::SetTrailingSignalsOnAutoSigsRoute
void SetTrailingSignalsOnAutoSigsRoute(int Caller, int TrackVectorPosition, int XLinkPos)
Enter with signal at TrackVectorElement already set to red by the passing train.
Definition: TrackUnit.cpp:18471
clB5G5R5
#define clB5G5R5
Definition: GraphicUnit.h:286
TUtilities::TimeStamp
AnsiString TimeStamp()
creates a string of the form 'hh:mm:ss' for use in call & event logging
Definition: Utilities.cpp:73
TTrainController::RandomFailureCounter
unsigned int RandomFailureCounter
new at v2.4.0, resets after 53 seconds (53 prime so can trigger at any clock time)
Definition: TrainUnit.h:814
TAllRoutes::TLockedRouteClass::RouteNumber
int RouteNumber
the vector position number of the relevant route in AllRoutesVector
Definition: TrackUnit.h:1617
TTrainController::AddTrain
bool AddTrain(int Caller, int RearPosition, int FrontPosition, AnsiString HeadCode, int StartSpeed, int Mass, double MaxRunningSpeed, double MaxBrakeRate, double PowerAtRail, AnsiString ModeStr, TTrainDataEntry *TrainDataEntryPtr, int RepeatNumber, int IncrementalMinutes, int IncrementalDigits, int SignallerSpeed, bool SignallerControl, TActionEventType &EventType)
Introduce a new train to the railway, with the characteristics specified, returns true for success,...
Definition: TrainUnit.cpp:9125
TTrainController::~TTrainController
~TTrainController()
Destructor.
Definition: TrainUnit.cpp:8740
TTrainController::ControllerCheckNewServiceDepartureTime
AnsiString ControllerCheckNewServiceDepartureTime(int Caller, TActionVectorIterator Ptr, int RptNum, TTrainDataEntry *TDEPtr, TTrainDataEntry *LinkedTrainDataPtr, int IncrementalMinutes, AnsiString RetStr)
Similar to TTrain::CheckNewServiceDepartureTime for use in ContinuationEntryFloatingTTString.
Definition: TrainUnit.cpp:9632
TTrainController::TwoLocationList
TServiceCallingLocsList TwoLocationList
Definition: TrainUnit.h:808
TUtilities::CheckAndReadFileInt
bool CheckAndReadFileInt(std::ifstream &InFile, int Lowest, int Highest, int &OutInt)
checks that the value is an int lying between Lowest & Highest (inclusive), returns true for success ...
Definition: Utilities.cpp:280
TRailGraphics::Code7
Graphics::TBitmap * Code7
Definition: GraphicUnit.h:961
TTrackElement::ActiveTrackElementName
AnsiString ActiveTrackElementName
Location name used either in the timetable or for a continuation (continuation names not used in time...
Definition: TrackUnit.h:129
TRailGraphics::bm94set
Graphics::TBitmap * bm94set
Definition: GraphicUnit.h:516
TTrainController::LogEvent
void LogEvent(AnsiString Str)
store Str to the event log - moved from TUtilities for v0.6 so can record the tt clock value
Definition: TrainUnit.cpp:8751
TRailGraphics::CodeO
Graphics::TBitmap * CodeO
Definition: GraphicUnit.h:978
TAllRoutes::SignallerRemovedTrainAutoRoute
TOneRoute SignallerRemovedTrainAutoRoute
if train was on an AutoSigsRoute when removed then this stores the route so that signals can be reset
Definition: TrackUnit.h:1704
TTrainController::MassHigh
bool MassHigh
Definition: TrainUnit.h:756
TTrainController::CheckAndPopulateListOfIDs
bool CheckAndPopulateListOfIDs(int Caller, AnsiString IDSet, TNumList &ExitList, bool GiveMessages)
Used to compile ExitList from a string list of element IDs, returns true for success or gives a messa...
Definition: TrainUnit.cpp:11082
TTrain::PlotTrainWithNewBackgroundColour
void PlotTrainWithNewBackgroundColour(int Caller, TColor NewBackgroundColour, TDisplay *Disp)
Changes the train's background colour (e.g. to pale green if stopped at a station) Note that this use...
Definition: TrainUnit.cpp:3397
TUtilities::SaveFileString
void SaveFileString(std::ofstream &OutFile, AnsiString SaveString)
stores the string value to the file, then a '0' delimiter then a CR
Definition: Utilities.cpp:135
TTrain::PlotAlternativeTrackRouteGraphic
void PlotAlternativeTrackRouteGraphic(int Caller, unsigned int LagElement, int LagELinkPos, int HOffset, int VOffset, TStraddle StraddleValue)
When a train moves off a bridge the other track may contain a route or have a train on it that has be...
Definition: TrainUnit.cpp:3097
TTrain::ExitTimeFull
TDateTime ExitTimeFull
times used in SetTrainMovementValues corresponding to the next element the train runs on
Definition: TrainUnit.h:424
TTrack::GapFlashRed
TGraphicElement * GapFlashRed
the red & green circle graphics used to show where the gaps are
Definition: TrackUnit.h:798
LocTypeForRepeatEntry
@ LocTypeForRepeatEntry
Definition: TrainUnit.h:70
SignallerChangeDirection
@ SignallerChangeDirection
Definition: TrainUnit.h:52
TAllRoutes::TLockedRouteClass
Handles routes that are locked because of approaching trains.
Definition: TrackUnit.h:1615
TTrain::TrainAtLocation
bool TrainAtLocation(int Caller, AnsiString &LocationName)
True when the train is stopped at a timetabled location.
Definition: TrainUnit.cpp:8316
TTrain::IsTrainIDOnBridgeTrackPos01
bool IsTrainIDOnBridgeTrackPos01(int Caller, unsigned int TrackVectorPosition)
True if train is on a bridge on trackpos 0 & 1.
Definition: TrainUnit.cpp:2961
TActionVectorEntry
Contains a single train action in a timetable - repeat entry is also of this class though no train ac...
Definition: TrainUnit.h:101
TTrainController::TotLateExitMins
float TotLateExitMins
Definition: TrainUnit.h:775
TTrainController::CheckSessionContinuationAutoSigEntries
bool CheckSessionContinuationAutoSigEntries(int Caller, std::ifstream &SessionFile)
Part of the session file integrity check for ContinuationAutoSigEntries, true for success.
Definition: TrainUnit.cpp:15163
TOneCompleteFormattedTrain::OneFormattedTrainVector
TOneFormattedTrainVector OneFormattedTrainVector
Definition: TrainUnit.h:245
TRailGraphics::Code1
Graphics::TBitmap * Code1
Definition: GraphicUnit.h:955
TTrainController::TimetableMessage
void TimetableMessage(bool GiveMessages, AnsiString Message)
Sends a message to the user if GiveMessages is true, including ServiceReference (see above) if not nu...
Definition: TrainUnit.cpp:14527
TDisplay
Definition: DisplayUnit.h:48
TTrackElement::ConnLinkPos
int ConnLinkPos[4]
Connecting element link position (i.e. array positions of the connecting element links,...
Definition: TrackUnit.h:146
TTrainController::ContinuationAutoSigVector
TContinuationAutoSigVector ContinuationAutoSigVector
vector for TContinuationAutoSigEntry objects
Definition: TrainUnit.h:816
TTrain::StoppedWithoutPower
bool StoppedWithoutPower
Definition: TrainUnit.h:443
TTrain::NameInTimetableBeforeCDT
int NameInTimetableBeforeCDT(int Caller, AnsiString Name, bool &Stop)
Returns the number by which the train ActionVectorEntryPtr needs to be incremented to point to the lo...
Definition: TrainUnit.cpp:4525
TTrainFormattedInformation::Header
AnsiString Header
description, mass, power, brake rate etc
Definition: TrainUnit.h:257
TActionEventType
TActionEventType
Used for reporting error conditions & warnings.
Definition: TrainUnit.h:39
TTrainController::TrainExistsAtIdent
bool TrainExistsAtIdent(int Caller, int TrainID)
new at v2.4.0 return true if find the train (added at v2.4.0 as can select a removed train in OAListB...
Definition: TrainUnit.cpp:9457
SignallerJoin
@ SignallerJoin
Definition: TrainUnit.h:51
TTrain::RearStartElement
int RearStartElement
start TrackVectorPosition element for rear of train
Definition: TrainUnit.h:327
TTrain::StepForwardFlag
bool StepForwardFlag
set when the signaller command to step forward one element has been given
Definition: TrainUnit.h:375
TTrainController::SplitTrainInfo
bool SplitTrainInfo(int Caller, AnsiString TrainInfoStr, AnsiString &HeadCode, AnsiString &Description, int &StartSpeed, int &MaxRunningSpeed, int &Mass, double &MaxBrakeRate, double &PowerAtRail, int &SignallerSpeed, bool GiveMessages)
Parse a train information entry, return true for success; PowerAtRail changed to double& from int& at...
Definition: TrainUnit.cpp:11165
TTrainController::TotEarlyExitMins
float TotEarlyExitMins
Definition: TrainUnit.h:771
TFixedTrackPiece::Config
TConfiguration Config[4]
the type of link - see TConfiguration above
Definition: TrackUnit.h:98
TTrainController::Operate
void Operate(int Caller)
called every clock tick to introduce new trains and update existing trains
Definition: TrainUnit.cpp:8765
TTrackElement::TrainIDOnElement
int TrainIDOnElement
Definition: TrackUnit.h:154
TRailGraphics::smRed
Graphics::TBitmap * smRed
Definition: GraphicUnit.h:887
TTrain::PowerAtRail
double PowerAtRail
in Watts (taken as 80% of the train's Gross Power, i.e. that entered by the user)
Definition: TrainUnit.h:406
TRailGraphics::LCPlainMan
Graphics::TBitmap * LCPlainMan
Definition: GraphicUnit.h:731
TDisplay::GetOutputLog4
TLabel * GetOutputLog4()
Definition: DisplayUnit.h:159
TAllRoutes::RebuildRailwayFlag
bool RebuildRailwayFlag
this is set whenever a route has to be cancelled forcibly in order to force a ClearandRebuildRailway ...
Definition: TrackUnit.h:1685
TTrainDataEntry::MaxRunningSpeed
double MaxRunningSpeed
in km/h
Definition: TrainUnit.h:192
TUtilities::LoadFileInt
int LoadFileInt(std::ifstream &InFile)
loads an int value from the file
Definition: Utilities.cpp:162
TTrackElement::ElementID
AnsiString ElementID
the element identifier based on position in the railway
Definition: TrackUnit.h:131
TRailGraphics::Code6
Graphics::TBitmap * Code6
Definition: GraphicUnit.h:960
TTrainDataEntry::TrainOperatingDataVector
TTrainOperatingDataVector TrainOperatingDataVector
operating information for the train including all its repeats
Definition: TrainUnit.h:206
TTrackElement::VLoc
int VLoc
The h & v locations in the railway (top lh corner of the first build screen = 0,0)
Definition: TrackUnit.h:148
TTrainController::OperatingTrainLateMins
float OperatingTrainLateMins
total late minutes of operating trains on exit operation for locations not reached yet
Definition: TrainUnit.h:764
TTrack::GetNonPointsOppositeLinkPos
int GetNonPointsOppositeLinkPos(int LinkPosIn)
Return the corresponding link position (track always occupies either links 0 & 1 or 2 & 3)
Definition: TrackUnit.h:897
TTrain::UnplotTrain
void UnplotTrain(int Caller)
Unplot train from screen in zoomed-in mode.
Definition: TrainUnit.cpp:534
TTrain::IsThereAnAdjacentTrain
bool IsThereAnAdjacentTrain(int Caller, TTrain *&TrainToBeJoinedBy)
Definition: TrainUnit.cpp:4984
TTrain::TRSTime
TDateTime TRSTime
location departure time and 'train ready to start' time (TRSTime is 10 seconds before the ReleaseTime...
Definition: TrainUnit.h:426
TAllRoutes::TLockedRouteClass::LastTrackVectorPosition
unsigned int LastTrackVectorPosition
the TrackVector position of the last (i.e. most forward) element in the route
Definition: TrackUnit.h:1621
FailMissedJBO
@ FailMissedJBO
Definition: TrainUnit.h:41
TUtilities::EventLog
std::deque< AnsiString > EventLog
event store, saved to the errorlog for diagnostic purposes
Definition: Utilities.h:57
TAllRoutes::NoRoute
@ NoRoute
Definition: TrackUnit.h:1631
TActionVectorEntry::FormatType
TTimetableFormatType FormatType
defines the timetable action type
Definition: TrainUnit.h:119
TTrain::RemainHereLogNotSent
bool RemainHereLogNotSent
flag to prevent repeated logs, new at v1.2.0
Definition: TrainUnit.h:306
TRailGraphics::CodeU
Graphics::TBitmap * CodeU
Definition: GraphicUnit.h:984
TTrainController::RestartTime
TDateTime RestartTime
TTClockTime when operation pauses ( = timetable start time prior to operation) TTClockTime is calcula...
Definition: TrainUnit.h:657
TTrain::NewShuttleFromNonRepeatService
void NewShuttleFromNonRepeatService(int Caller)
Carry out the actions needed when a new shuttle service is created from a non-repeating (F-nshs) serv...
Definition: TrainUnit.cpp:6537
TTrainController::TimetableStartTime
TDateTime TimetableStartTime
the start time of the current timetable
Definition: TrainUnit.h:655
clSPADBackground
#define clSPADBackground
Definition: GraphicUnit.h:300
TTrainController::UnplotTrains
void UnplotTrains(int Caller)
unplot all trains from screen
Definition: TrainUnit.cpp:9109
TRailGraphics::smBrightGreen
Graphics::TBitmap * smBrightGreen
Definition: GraphicUnit.h:878
TTrainController::EarlyArrivals
int EarlyArrivals
Definition: TrainUnit.h:780
FailCreateTrain
@ FailCreateTrain
Definition: TrainUnit.h:40
TTrain::IncrementalDigits
int IncrementalDigits
the number of digits to increment by in repeat entries
Definition: TrainUnit.h:323
TTrainController::CreateFormattedTimetable
void CreateFormattedTimetable(int Caller, AnsiString RailwayTitle, AnsiString TimetableTitle, AnsiString CurDir)
Examines the internal timetable (TrainDataVector) and creates from it a chronological (....
Definition: TrainUnit.cpp:15330
TTrack::RouteFlashFlag
bool RouteFlashFlag
true while a route is flashing prior to being set
Definition: TrackUnit.h:765
TRailGraphics::Code_v
Graphics::TBitmap * Code_v
Definition: GraphicUnit.h:949
TTrackElement::StationEntryStopLinkPos1
int StationEntryStopLinkPos1
Definition: TrackUnit.h:152
Points
@ Points
Definition: TrackUnit.h:67
TTrainController::SplitEntry
bool SplitEntry(int Caller, AnsiString OneEntry, bool GiveMessages, bool CheckLocationsExistInRailway, AnsiString &First, AnsiString &Second, AnsiString &Third, AnsiString &Fourth, int &RearStartOrRepeatMins, int &FrontStartPosition, TTimetableFormatType &TimetableFormatType, TTimetableLocationType &LocationType, TTimetableSequenceType &SequenceType, TTimetableShuttleLinkType &ShuttleLinkType, TNumList &ExitList, bool &Warning)
Parse a single timetable service action, return true for success.
Definition: TrainUnit.cpp:10674
TRailGraphics::LCPlain
Graphics::TBitmap * LCPlain
Definition: GraphicUnit.h:724
TTrain::LastActionDelayFlag
bool LastActionDelayFlag
used when trains join to ensure that there is a 30 second delay before the actual join takes place af...
Definition: TrainUnit.h:363
TTrainController::TotLatePassMins
float TotLatePassMins
Definition: TrainUnit.h:774
TTrainController::StripSpaces
void StripSpaces(int Caller, AnsiString &Input)
Strip both leading and trailing spaces at ends of Input and spaces before and after all commas and se...
Definition: TrainUnit.cpp:13645
FailMissedJoinOther
@ FailMissedJoinOther
Definition: TrainUnit.h:41
TTrain::RepeatShuttleOrRemainHere
void RepeatShuttleOrRemainHere(int Caller)
Carry out the actions needed to create either a new shuttle service or (if all repeats have finished)...
Definition: TrainUnit.cpp:6578
TTrain::JoinedOtherTrainFlag
bool JoinedOtherTrainFlag
true when the train has joined another train following an 'Fjo' timetable command or a signaller join...
Definition: TrainUnit.h:361
TTrainController::BFLow
bool BFLow
Definition: TrainUnit.h:756
TTrainController::TotArrDepPass
int TotArrDepPass
Definition: TrainUnit.h:796
SignallerPassRedSignal
@ SignallerPassRedSignal
Definition: TrainUnit.h:52
TRailGraphics::ChangeForegroundColour
void ChangeForegroundColour(int Caller, Graphics::TBitmap *BitmapIn, Graphics::TBitmap *BitmapOut, TColor NewForegroundColour, TColor BackgroundColour)
Definition: GraphicUnit.cpp:3362
TRailGraphics::CodeE
Graphics::TBitmap * CodeE
Definition: GraphicUnit.h:968
FailLockedRoute
@ FailLockedRoute
Definition: TrainUnit.h:40
TTrain::SignallerRemoved
bool SignallerRemoved
set when removed under signaller control to force a removal from the display at the next clock tick
Definition: TrainUnit.h:369
clB5G3R0
#define clB5G3R0
Definition: GraphicUnit.h:267
TTrain::FrontCodePtr
Graphics::TBitmap * FrontCodePtr
points to the front headcode segment, this is set to red or blue depending on TrainMode
Definition: TrainUnit.h:460
TOneRoute::SetRouteSignals
void SetRouteSignals(int Caller) const
Called when setting a route to set all points appropriately. Also called when a new train is added at...
Definition: TrackUnit.cpp:16717
Continuation
@ Continuation
Definition: TrackUnit.h:67
TTrainController::CallOnWarning
bool CallOnWarning
Definition: TrainUnit.h:742
TTrack::GetFilletGraphic
Graphics::TBitmap * GetFilletGraphic(int Caller, TTrackElement TrackElement)
Return a pointer to the point fillet (the bit that appears to move when points are changed) for the p...
Definition: TrackUnit.cpp:7482
GraphicUnit.h
TTrack::PointFlashFlag
bool PointFlashFlag
true when points are flashing during manual change
Definition: TrackUnit.h:763
TTrainController::OnTimeDeps
int OnTimeDeps
Definition: TrainUnit.h:790
TTrain::RestoreTimetableLocation
AnsiString RestoreTimetableLocation
stores the location name at which signaller control is taken, to ensure that it is back at that locat...
Definition: TrainUnit.h:434
TTrainController::TimetableIntegrityCheck
bool TimetableIntegrityCheck(int Caller, char *FileName, bool GiveMessages, bool CheckLocationsExistInRailway)
Checks overall timetable integrity, calls many other specific checking functions, returns true for su...
Definition: TrainUnit.cpp:9811
AllRoutes
TAllRoutes * AllRoutes
the object pointer, object created in InterfaceUnit
Definition: TrackUnit.cpp:54
TTrain::SignallerMaxSpeed
int SignallerMaxSpeed
maximum train speed under signaller control (in km/h)
Definition: TrainUnit.h:333
NoEvent
@ NoEvent
Definition: TrainUnit.h:40
FailSplitDueToOtherTrain
@ FailSplitDueToOtherTrain
Definition: TrainUnit.h:40
TTrainController::SendPerformanceSummary
void SendPerformanceSummary(int Caller, std::ofstream &PerfFile)
At the end of operation a summary of overall performance is sent to the performance file by this func...
Definition: TrainUnit.cpp:18784
TTrain::ResetTrainElementID
void ResetTrainElementID(int Caller, unsigned int TrackVectorPosition, int EntryPos)
After a train has moved off an element that element has its TrainIDOnElement value set back to -1 to ...
Definition: TrainUnit.cpp:3055
TRailGraphics::smMagenta
Graphics::TBitmap * smMagenta
Definition: GraphicUnit.h:883
RepairFailedTrain
@ RepairFailedTrain
Definition: TrainUnit.h:52
TTrainController::AllServiceCallingLocsMap
TAllServiceCallingLocsMap AllServiceCallingLocsMap
Definition: TrainUnit.h:725
TRailGraphics::smPaleGreen
Graphics::TBitmap * smPaleGreen
Definition: GraphicUnit.h:886
TRailGraphics::CodeS
Graphics::TBitmap * CodeS
Definition: GraphicUnit.h:982
TTrainController::ContinuationEntryFloatingTTString
AnsiString ContinuationEntryFloatingTTString(int Caller, TTrainDataEntry *TTDEPtr, int RepeatNumber, int IncrementalMinutes, int IncrementalDigits)
Build string for use in floating window for expected trains at continuations.
Definition: TrainUnit.cpp:9488
TFixedTrackPiece::SpeedTag
int SpeedTag
The element identification number - corresponds to the relevant SpeedButton->Tag.
Definition: TrackUnit.h:89
TTrain::SPADFlag
bool SPADFlag
set when running past a red signal without permission flags to indicate relevant stop conditions or p...
Definition: TrainUnit.h:440
TTrain::Stopped
bool Stopped()
True if the train has stopped for any reason.
Definition: TrainUnit.h:624
TTrack::SigTableGroundSignal
TSigElement SigTableGroundSignal[40]
new at version 0.6 for ground signals
Definition: TrackUnit.h:740
TTrain::TrainToJoinIsAdjacent
bool TrainToJoinIsAdjacent(int Caller, TTrain *&TrainToJoin)
True for a train waiting to join another when the other train is adjacent.
Definition: TrainUnit.cpp:6480
TActionVectorEntry::ExitList
TNumList ExitList
the list of valid train exit TrackVector positions for 'Fer' entries (empty to begin with)
Definition: TrainUnit.h:117
TTrainController::OnTimeExits
int OnTimeExits
Definition: TrainUnit.h:792
TTrain::OldZoomOutElement
int OldZoomOutElement[3]
stores the Lead, Mid & Lag TrackVectorPositions, used for unplotting trains from the old position in ...
Definition: TrainUnit.h:447
TPrefDirElement::GetELink
int GetELink() const
Returns ELink.
Definition: TrackUnit.h:281
TDisplay::GetOutputLog8
TLabel * GetOutputLog8()
Definition: DisplayUnit.h:179
WaitingForJBO
@ WaitingForJBO
Definition: TrainUnit.h:43
TUtilities::CheckFileDouble
bool CheckFileDouble(std::ifstream &InFile)
checks that the value is a double, returns true for success
Definition: Utilities.cpp:331
TTrain::Straddle
TStraddle Straddle
the current Straddle value of the train (see TStraddle above)
Definition: TrainUnit.h:467
SignallerStop
@ SignallerStop
Definition: TrainUnit.h:52
TRailGraphics::CodeB
Graphics::TBitmap * CodeB
Definition: GraphicUnit.h:965
TAllRoutes::DiagonalFouledByRouteOrTrain
bool DiagonalFouledByRouteOrTrain(int Caller, int HLoc, int VLoc, int DiagonalLinkNumber)
The track geometry allows diagonals to cross without occupying the same track element,...
Definition: TrackUnit.cpp:19135
TTrackElement::TempTrackMarker01
bool TempTrackMarker01
Definition: TrackUnit.h:140
Display
TDisplay * Display
The object pointer for the on-screen display, object created in InterfaceUnit.
Definition: DisplayUnit.cpp:53
TTrainController::GetServiceFromVector
TTrainDataEntry GetServiceFromVector(AnsiString Caller, AnsiString ServiceReference, TTrainDataVector Vector, bool &FinishType, bool &FoundFlag)
Definition: TrainUnit.cpp:18038
TTrack::GetVLocMin
int GetVLocMin()
Definition: TrackUnit.h:891
TRailGraphics::Code3
Graphics::TBitmap * Code3
Definition: GraphicUnit.h:957
TUtilities::CheckFileBool
bool CheckFileBool(std::ifstream &InFile)
checks that the value is a bool returns true for success
Definition: Utilities.cpp:209
TTrack::OtherTrainOnTrack
bool OtherTrainOnTrack(int Caller, int NextPos, int NextEntryPos, int OwnTrainID)
True if another train on NextEntryPos track of element at NextPos, whether bridge or not,...
Definition: TrackUnit.cpp:10862
TTrain::MaximumPowerLimit
static const int MaximumPowerLimit
Watts (i.e. 100MW)
Definition: TrainUnit.h:292
TTrain::PlotStartPosition
void PlotStartPosition(int Caller)
Plots the train and sets up all relevant members for a new train when it is introduced into the railw...
Definition: TrainUnit.cpp:261
TTrack::TActiveTrackElementNameMapEntry
std::pair< AnsiString, int > TActiveTrackElementNameMapEntry
Definition: TrackUnit.h:720
TTrainController::SingleServiceOutput
void SingleServiceOutput(int Caller, int SSVectorNumber, TNumList MarkerList, TTrainDataVector &SingleServiceVector, std::ofstream &VecFile)
Outputs the single service vector for train direction analysis purposes in timetable conflict analysi...
Definition: TrainUnit.cpp:17931
TTrainController::TContinuationAutoSigEntry::SecondDelay
double SecondDelay
Definition: TrainUnit.h:666
TTrain::UnplotTrainInZoomOutMode
void UnplotTrainInZoomOutMode(int Caller)
Unplot train from screen in zoomed-out mode.
Definition: TrainUnit.cpp:8279
TTimetableFormatType
TTimetableFormatType
Timetable entry types.
Definition: TrainUnit.h:63
TTrainController::TrainFailedWarning
bool TrainFailedWarning
Flags to enable the relevant warning graphics to flash at the left hand side of the screen.
Definition: TrainUnit.h:742
TTrain::NextTrainID
static int NextTrainID
the ID value to be used for the next train that is created, static so that it doesn't need an object ...
Definition: TrainUnit.h:297
TTrainController::TLocServiceTimes
Class used for timetable conflict file compilation.
Definition: TrainUnit.h:711
TTrainOperatingData::RunningEntry
TRunningEntry RunningEntry
Definition: TrainUnit.h:166
NotAShuttleLink
@ NotAShuttleLink
Definition: TrainUnit.h:80
TRailGraphics::gl91set
Graphics::TBitmap * gl91set
Definition: GraphicUnit.h:707
TRailGraphics::Code_y
Graphics::TBitmap * Code_y
Definition: GraphicUnit.h:952
TAllRoutes::CheckMapAndRoutes
void CheckMapAndRoutes(int Caller)
Diagnostic function - checks equivalence for each route between entries in PrefDirVector and those in...
Definition: TrackUnit.cpp:18244
TAllRoutes::GetRouteElementDataFromRoute2MultiMap
TRouteElementPair GetRouteElementDataFromRoute2MultiMap(int Caller, int HLoc, int VLoc, TRouteElementPair &SecondPair)
Retrieve up to two TRouteElementPair entries from Route2MultiMap at H & V, the first as a function re...
Definition: TrackUnit.cpp:18200
TTrainController::TrainAdded
bool TrainAdded
true when a train has been added by a split (occurs outside the normal train introduction process)
Definition: TrainUnit.h:746
TActionVectorEntry::LocationName
AnsiString LocationName
Definition: TrainUnit.h:103
TTrainFormattedInformation::OneCompleteFormattedTrainVector
TOneCompleteFormattedTrainVector OneCompleteFormattedTrainVector
Definition: TrainUnit.h:261
ShuttleFinishedRemainingHere
@ ShuttleFinishedRemainingHere
Definition: TrainUnit.h:43
FailUnexpectedBuffers
@ FailUnexpectedBuffers
Definition: TrainUnit.h:41
Start
@ Start
Definition: TrainUnit.h:75
TRailGraphics::TempBackground
Graphics::TBitmap * TempBackground
Definition: GraphicUnit.h:890
TTrain::TimeTimeLocArrived
bool TimeTimeLocArrived
indicates whether has arrived (true) or not when ActionVectorEntryPtr->FormatType == TimeTimeLoc
Definition: TrainUnit.h:304
TActionType
TActionType
Used in LogAction when reporting a train action to the performance log & file.
Definition: TrainUnit.h:49
TTrainController::TContinuationAutoSigEntry::ThirdDelay
double ThirdDelay
Delays in seconds before consecutive signal changes - these correspond to the times taken for trains ...
Definition: TrainUnit.h:666
TTrain::LeadEntryPos
int LeadEntryPos
Definition: TrainUnit.h:337
TTrainController::LateArrivals
int LateArrivals
Definition: TrainUnit.h:784
TRailGraphics::CodeZ
Graphics::TBitmap * CodeZ
Definition: GraphicUnit.h:989
NoShuttleLink
@ NoShuttleLink
Definition: TrainUnit.h:80
TTrainController::ReplotTrains
void ReplotTrains(int Caller, TDisplay *Disp)
plot all trains on the display
Definition: TrainUnit.cpp:9079
TTrainController::CheckShuttleServiceIntegrity
bool CheckShuttleServiceIntegrity(int Caller, TTrainDataEntry *TDEntryPtr, bool GiveMessages)
Check that each shuttle service ends either in Fns or Fxx-sh (though a single service can't end in Fx...
Definition: TrainUnit.cpp:14474
TTrain::FloatingTimetableString
AnsiString FloatingTimetableString(int Caller, TActionVectorEntry *Ptr)
Used in the floating window to display the timetable.
Definition: TrainUnit.cpp:7054
TAllRoutes::GetModifiableRouteAt
TOneRoute & GetModifiableRouteAt(int Caller, int At)
Returns a modifiable reference to the route at AllRoutesVector position 'At', after performing range ...
Definition: TrackUnit.cpp:17470
TTrain::AValue
double AValue
this is a useful shorthand value in calculating speeds and transit times in SetTrainMovementValues [=...
Definition: TrainUnit.h:383
FailUnexpectedExitRailway
@ FailUnexpectedExitRailway
Definition: TrainUnit.h:41
TTrain::FrontTrainSplit
void FrontTrainSplit(int Caller)
Carry out the actions needed when a train is to split from the front.
Definition: TrainUnit.cpp:5424
TTrainController::WithinTimeRange
bool WithinTimeRange(int Caller, AnsiString Time1, AnsiString Time2, int MinuteRange)
check whether the two times are within the range in minutes specified and return true if so....
Definition: TrainUnit.cpp:18074
TTrainController::WriteTrainsToImage
void WriteTrainsToImage(int Caller, Graphics::TBitmap *Bitmap)
Called by TInterface::SaveOperatingImage1Click) to write all trains to the image file.
Definition: TrainUnit.cpp:9094
TActionVectorEntry::LinkedTrainEntryPtr
TTrainDataEntry * LinkedTrainEntryPtr
link pointer for use between fsp/rsp & Sfs; Fjo & jbo; Fns & Sns; & all shuttle to shuttle links
Definition: TrainUnit.h:127
TTrainController::AtLocSuccessor
bool AtLocSuccessor(const TActionVectorEntry &AVEntry)
A shorthand function that returns true if the successor to a given timetable action command should be...
Definition: TrainUnit.cpp:13095
TTrain::ZeroPowerNoJoinedByMessage
bool ZeroPowerNoJoinedByMessage
Definition: TrainUnit.h:313
TTrain::CalcTimeToAct
float CalcTimeToAct(int Caller)
new v2.2.0 for operator action panel. Calculates the time left for operator action to avoid unnecessa...
Definition: TrainUnit.cpp:8423
TTrainController::SaveSessionContinuationAutoSigEntries
void SaveSessionContinuationAutoSigEntries(int Caller, std::ofstream &SessionFile)
save ContinuationAutoSigEntries to a session file
Definition: TrainUnit.cpp:15123
TTrainOperatingData::TrainID
int TrainID
Definition: TrainUnit.h:164
TDisplay::GetOutputLog3
TLabel * GetOutputLog3()
Definition: DisplayUnit.h:154
NoLocation
@ NoLocation
Definition: TrainUnit.h:70
TRailGraphics::bmTransparentBgnd
Graphics::TBitmap * bmTransparentBgnd
Definition: GraphicUnit.h:900
TTrain::FirstHalfMove
bool FirstHalfMove
true when the train is on the first half of an element when it displays as fully on two elements....
Definition: TrainUnit.h:359
FailLocTooShort
@ FailLocTooShort
Definition: TrainUnit.h:40
TTrainDataEntry::Mass
int Mass
in kg
Definition: TrainUnit.h:196
NoMode
@ NoMode
Definition: TrainUnit.h:58
TTrainController::LatePasses
int LatePasses
Definition: TrainUnit.h:786
TTrainController::SplitRepeat
bool SplitRepeat(int Caller, AnsiString OneEntry, int &RearStartOrRepeatMins, int &FrontStartOrRepeatDigits, int &RepeatNumber, bool GiveMessages)
Parse a timetable repeat entry, return true for success.
Definition: TrainUnit.cpp:11513
TFixedTrackPiece::TrackType
TTrackType TrackType
the type of track element
Definition: TrackUnit.h:101
TTrainController::TLocServiceTimes::ArrTime
AnsiString ArrTime
Definition: TrainUnit.h:715
TTrainController::SSHigh
bool SSHigh
Definition: TrainUnit.h:756
TimeLoc
@ TimeLoc
Definition: TrainUnit.h:64
TActionVectorIterator
TActionVector::iterator TActionVectorIterator
iterator
Definition: TrainUnit.h:155
TPrefDirElement
Basic preferred direction or route element - track element with additional members.
Definition: TrackUnit.h:213
TTrainController::AvHoursIntValue
int AvHoursIntValue
Input in MTBFEditBox in timetable hours, min value is 1 and max is 10,000. Here because performance f...
Definition: TrainUnit.h:806
TTrainController::CrashedTrains
int CrashedTrains
Definition: TrainUnit.h:778
TTrainController::TTEditPanelVisible
bool TTEditPanelVisible
new at v2.6.0 so potential error message only shows in TTEdit mode
Definition: TrainUnit.h:754
TDisplay::WarningLog
void WarningLog(int Caller, AnsiString Statement)
Display warning message Statement in the bottom left hand warning position and scroll other messages ...
Definition: DisplayUnit.cpp:531
TTrain::LogAction
void LogAction(int Caller, AnsiString HeadCode, AnsiString OtherHeadCode, TActionType ActionType, AnsiString LocationName, TDateTime TimetableNonRepeatTime, bool Warning)
Send a message to the performance log and performance file, and if the message is flagged as a warnin...
Definition: TrainUnit.cpp:5068
TTrain::StoppedAtSignal
bool StoppedAtSignal
Definition: TrainUnit.h:442
TTrainController::GetControllerTrainTime
TDateTime GetControllerTrainTime(int Caller, TDateTime Time, int RepeatNumber, int IncrementalMinutes)
Get the interval between repeats.
Definition: TrainUnit.cpp:9476
TRailGraphics::Code_u
Graphics::TBitmap * Code_u
Definition: GraphicUnit.h:948
TTrackElement::Attribute
int Attribute
special variable used only for points, signals & level crossings, ignored otherwise; points 0=set to ...
Definition: TrackUnit.h:142
TStraddle
TStraddle
Defines the train position with respect to the track elements; three consecutive elements are Lead (f...
Definition: TrainUnit.h:276
FailEntryRouteSetAgainst
@ FailEntryRouteSetAgainst
Definition: TrainUnit.h:44
TTrainController::THCandTrainPosParam
std::pair< AnsiString, int > THCandTrainPosParam
Definition: TrainUnit.h:727
TTrainDataVector
std::vector< TTrainDataEntry > TTrainDataVector
vector class for containing the whole timetable - one entry per timetable service entry (the object i...
Definition: TrainUnit.h:219
TTrainController::GetRepeatTime
TDateTime GetRepeatTime(int Caller, TDateTime BasicTime, int RepeatNumber, int IncMinutes)
Return the repeating service time.
Definition: TrainUnit.cpp:14153
clCrashedBackground
#define clCrashedBackground
Definition: GraphicUnit.h:293
TTrack::IsLCAtHV
bool IsLCAtHV(int Caller, int HLoc, int VLoc)
True if a level crossing is found at H & V.
Definition: TrackUnit.cpp:7221
TTrain::OpTimeToAct
float OpTimeToAct
in minutes: new at v2.2.0 for operator time to act panel. Calculated in UpdateTrain,...
Definition: TrainUnit.h:412
TTrainController::TTClockTime
TDateTime TTClockTime
the time indicated by the timetable clock
Definition: TrainUnit.h:653
TAllRoutes::TLockedRouteClass::LockStartTime
TDateTime LockStartTime
the timetable time at which the route is locked, to start the 2 minute clock
Definition: TrackUnit.h:1625
TActionVectorEntry::SequenceType
TTimetableSequenceType SequenceType
indicates where in the sequence of codes the action lies
Definition: TrainUnit.h:123
TTrain::RearStartExitPos
int RearStartExitPos
the LinkPos value for the rear starting element (i.e. links to the front starting element)
Definition: TrainUnit.h:329
TTrainController::TLocServiceTimes::FrhMarker
AnsiString FrhMarker
Definition: TrainUnit.h:717
TRailGraphics::Code_o
Graphics::TBitmap * Code_o
Definition: GraphicUnit.h:942
TRailGraphics::CodeV
Graphics::TBitmap * CodeV
Definition: GraphicUnit.h:985
TTrainController::TLocServiceTimes::ServiceAndRepeatNum
AnsiString ServiceAndRepeatNum
Definition: TrainUnit.h:713
TTrain::SetTrainElementID
void SetTrainElementID(int Caller, unsigned int TrackVectorPosition, int EntryPos)
When a train moves onto an element that element has its TrainIDOnElement value set to the TrainID val...
Definition: TrainUnit.cpp:3019
TActionVectorEntry::NonRepeatingShuttleLinkHeadCode
AnsiString NonRepeatingShuttleLinkHeadCode
string values for timetabled action entries, null on creation
Definition: TrainUnit.h:103
TActionVectorEntry::ShuttleLinkType
TTimetableShuttleLinkType ShuttleLinkType
indicates whether or not the action relates to a shuttle service link
Definition: TrainUnit.h:125
TTrain::TimetableFinished
bool TimetableFinished
set when there are no more timetable actions
Definition: TrainUnit.h:379
TUtilities::CallLog
std::deque< AnsiString > CallLog
call stack store, saved to the errorlog for diagnostic purposes
Definition: Utilities.h:55
TRailGraphics::CodeN
Graphics::TBitmap * CodeN
Definition: GraphicUnit.h:977
TActionVectorEntry::Command
AnsiString Command
Definition: TrainUnit.h:103
TTrain::CallingOnAllowed
bool CallingOnAllowed(int Caller)
True if the train can be called on at its current position - see detail in .cpp file.
Definition: TrainUnit.cpp:4692
TTrainController::TContinuationTrainExpectationEntry::HeadCode
AnsiString HeadCode
service headcode
Definition: TrainUnit.h:686
TTrain::RepeatShuttleOrNewNonRepeatService
void RepeatShuttleOrNewNonRepeatService(int Caller)
Carry out the actions needed to create either a new shuttle service or (if all repeats have finished)...
Definition: TrainUnit.cpp:6640
TTrackElement::Length23
int Length23
Definition: TrackUnit.h:150
TTrain::SignallerStoppingFlag
bool SignallerStoppingFlag
set when the signaller stop command has been given
Definition: TrainUnit.h:371
TTrainController::NotStartedTrainLateArr
int NotStartedTrainLateArr
total number of arrivals & departures for trains that haven't started yet for locations not reached y...
Definition: TrainUnit.h:802
TUtilities::LoadFileBool
bool LoadFileBool(std::ifstream &InFile)
loads a bool value from the file
Definition: Utilities.cpp:145
TTrainController::SigSLow
bool SigSLow
Message flags in TT checks to stop being given twice.
Definition: TrainUnit.h:756
TRailGraphics::Code_m
Graphics::TBitmap * Code_m
Definition: GraphicUnit.h:940
TTrack::AnyLinkedBarrierDownVectorManual
bool AnyLinkedBarrierDownVectorManual(int Caller, int HLoc, int VLoc, int &BDVectorPos)
Checks BarrierDownVector and returns true if there is one that is linked to the LC at H & V positions...
Definition: TrackUnit.cpp:6245
TRailGraphics::gl92set
Graphics::TBitmap * gl92set
Definition: GraphicUnit.h:709
SNSShuttle
@ SNSShuttle
Definition: TrainUnit.h:64
RemoveTrain
@ RemoveTrain
Definition: TrainUnit.h:51
TTrainController::TLocServiceTimes::DepTime
AnsiString DepTime
Definition: TrainUnit.h:716
TTrainController::SetWarningFlags
void SetWarningFlags(int Caller)
This sets all the warning flags (CrashWarning, DerailWarning etc) to their required states after a se...
Definition: TrainUnit.cpp:19203
TDisplay::PlotSmallOutput
void PlotSmallOutput(int Caller, int HPos, int VPos, Graphics::TBitmap *PlotItem)
Plot small (4x4) graphic PlotItem on the zoomed-out display at HPos & Vpos.
Definition: DisplayUnit.cpp:113
TTrainController::TContinuationTrainExpectationEntry
Class that stores data for trains expected at continuation entries (kept in a multimap - see below),...
Definition: TrainUnit.h:682
SNSNonRepeatFromShuttle
@ SNSNonRepeatFromShuttle
Definition: TrainUnit.h:64
TTrainController::TotEarlyPassMins
float TotEarlyPassMins
Definition: TrainUnit.h:770
TRailGraphics::CodeH
Graphics::TBitmap * CodeH
Definition: GraphicUnit.h:971
TTrainController::OpTimeToActMultiMap
TOpTimeToActMultiMap OpTimeToActMultiMap
added v2.2.0 for Op time to act display
Definition: TrainUnit.h:820
TTrainController::TContinuationAutoSigEntry::AccessNumber
int AccessNumber
the number of times the signal changing function has been accessed - starts at 0 and increments after...
Definition: TrainUnit.h:668
TActionVectorEntry::SignallerControl
bool SignallerControl
indicates a train that is defined by the timetable as under signaller control
Definition: TrainUnit.h:107
TTrain::GetTrainTime
TDateTime GetTrainTime(int Caller, TDateTime Time)
Returns the timetable action time corresponding to 'Time' for this train, i.e. it adjusts the time va...
Definition: TrainUnit.cpp:4973
TTrain::BrakeRate
double BrakeRate
the current train brake rate
Definition: TrainUnit.h:401
TTrackElement::SpeedLimit23
int SpeedLimit23
Element lengths and speed limits, ...01 is for the track with link positions [0] and [1],...
Definition: TrackUnit.h:150
TTrainController::TContinuationAutoSigEntry::PassoutTime
TDateTime PassoutTime
the timetable clock time at which the train exits from the continuation
Definition: TrainUnit.h:672
TTrain::FinishJoinLogSent
bool FinishJoinLogSent
Definition: TrainUnit.h:308
TTrack::GapFlashFlag
bool GapFlashFlag
true when a pair of connected gaps is flashing
Definition: TrackUnit.h:751
TRailGraphics::gl95set
Graphics::TBitmap * gl95set
Definition: GraphicUnit.h:713
TrainUnit.h
PassTime
@ PassTime
Definition: TrainUnit.h:65
TTrainController::IncorrectExits
int IncorrectExits
Definition: TrainUnit.h:783
TTrain::ContinuationExit
bool ContinuationExit(int Caller, int Element, int Exitpos) const
True if Element is a continuation and Exitpos is the continuation end.
Definition: TrainUnit.cpp:2943
TFixedTrackPiece::Link
int Link[4]
Track connection link values, max. of 4, unused = -1, top lh diag.=1, top=2, top rh diag....
Definition: TrackUnit.h:91
TTrain::UpdateTrain
void UpdateTrain(int Caller)
Major function called at each clock tick for each train & handles all train movement & associated act...
Definition: TrainUnit.cpp:626
TRailGraphics::LockedRouteCancelPtr
Graphics::TBitmap * LockedRouteCancelPtr[10]
for locked route cancel graphic, 1 for each of 8 links, 0 & 5 included as for direction
Definition: GraphicUnit.h:1038
TRailGraphics::Code8
Graphics::TBitmap * Code8
Definition: GraphicUnit.h:962
TTrainController::SameDirection
bool SameDirection(int Caller, AnsiString Ref1, AnsiString Ref2, AnsiString Time1, AnsiString Time2, int RepeatNum1, int RepeatNum2, TServiceCallingLocsList List1, TServiceCallingLocsList List2, AnsiString Location, bool Arrival)
Determines whether two services are running in the same direction when they arrive or depart from Loc...
Definition: TrainUnit.cpp:18358
TTrain::TrainToBeJoinedByIsAdjacent
bool TrainToBeJoinedByIsAdjacent(int Caller, TTrain *&TrainToBeJoinedBy)
True for a train waiting to be joined when the joining train is adjacent.
Definition: TrainUnit.cpp:6508
TTrain::SetTrainMovementValues
void SetTrainMovementValues(int Caller, int TrackVectorPosition, int EntryPos)
Calculates train speeds and times for the element that the train is about to enter....
Definition: TrainUnit.cpp:3439
TActionVectorEntry::Warning
bool Warning
if set triggers an alert in the warning panel when the action is reached
Definition: TrainUnit.h:109
TTrain::PickUpBackgroundBitmap
void PickUpBackgroundBitmap(int Caller, int HOffset, int VOffset, int Element, int EntryPos, Graphics::TBitmap *GraphicPtr) const
Store the background bitmap pointer (BackgroundPtr - see above) prior to being overwritten by the tra...
Definition: TrainUnit.cpp:2531
clStoppedTrainInFront
#define clStoppedTrainInFront
Definition: GraphicUnit.h:302
TDisplay::GetOutputLog10
TLabel * GetOutputLog10()
Definition: DisplayUnit.h:189
TTrainDataEntry::ServiceReference
AnsiString ServiceReference
Definition: TrainUnit.h:188
TTrain::SignallerChangeTrainDirection
void SignallerChangeTrainDirection(int Caller)
Unplots & replots train, which checks for facing signal and sets StoppedAtSignal if req'd.
Definition: TrainUnit.cpp:6821
TTrain::ZeroPowerNoRearSplitMessage
bool ZeroPowerNoRearSplitMessage
Definition: TrainUnit.h:311
FailMissedChangeDirection
@ FailMissedChangeDirection
Definition: TrainUnit.h:42
NotSet
@ NotSet
Definition: TrackUnit.h:77
Repeat
@ Repeat
Definition: TrainUnit.h:65
TRailGraphics::Code_c
Graphics::TBitmap * Code_c
Definition: GraphicUnit.h:930
TTrainController::StripExcessFromHeadCode
void StripExcessFromHeadCode(int Caller, AnsiString &HeadCode)
change an extended headcode to an ordinary 4 character headcode
Definition: TrainUnit.cpp:13104
TTrack::TIMPair
std::pair< unsigned int, unsigned int > TIMPair
TrackElement pair type used for inactive elements, values are vector positions.
Definition: TrackUnit.h:688
TUtilities::Clock2Stopped
bool Clock2Stopped
when true the main loop - Interface->ClockTimer2 - is stopped
Definition: Utilities.h:38
TTrainController::MRSHigh
bool MRSHigh
Definition: TrainUnit.h:756
TTrackElement::GroundSignal
@ GroundSignal
Definition: TrackUnit.h:159
LevelCrossing
@ LevelCrossing
Definition: TrackUnit.h:68
TDisplay::GetOutputLog2
TLabel * GetOutputLog2()
Definition: DisplayUnit.h:149
TTrain::NotInService
bool NotInService
Definition: TrainUnit.h:443
TRailGraphics::CodeA
Graphics::TBitmap * CodeA
Definition: GraphicUnit.h:964
TTrack::ActiveTrackElementNameMapCompiledFlag
bool ActiveTrackElementNameMapCompiledFlag
indicates that the ActiveTrackElementNameMap has been compiled
Definition: TrackUnit.h:745
TTrainController::LocServiceTimesLocationSort
bool LocServiceTimesLocationSort(TLocServiceTimes i, TLocServiceTimes j)
Definition: TrainUnit.h:831
TOneTrainFormattedEntry
A single train timetable action for use in a formatted timetable.
Definition: TrainUnit.h:227
TTrain::RearTrainSplit
void RearTrainSplit(int Caller)
Carry out the actions needed when a train is to split from the rear.
Definition: TrainUnit.cpp:5734
TTrainController::ConsolidateSARNTArrDep
AnsiString ConsolidateSARNTArrDep(int Caller, const AnsiString Input, int &NumTrainsAtLoc, AnsiString Location, bool Arrival, bool &AnalysisError, int &MaxNumberOfSameDirections)
Removes duplicates from and sorts ServiceAndRepeatNumTotal into alphabetical order for arrivals (bool...
Definition: TrainUnit.cpp:18100
Track
TTrack * Track
the object pointer, object created in InterfaceUnit
Definition: TrackUnit.cpp:53
TTrackElement::Conn
int Conn[4]
Connecting element position in TrackVector, set to -1 if no connecting link or if track not linked.
Definition: TrackUnit.h:144
Signaller
@ Signaller
Definition: TrainUnit.h:58
TTrain::TrainCrashedInto
int TrainCrashedInto
the TrainID of the train that this train has crashed into, recorded so that train can be marked and d...
Definition: TrainUnit.h:453
TRailGraphics::smYellow
Graphics::TBitmap * smYellow
Definition: GraphicUnit.h:888
RailGraphics
TRailGraphics * RailGraphics
the object pointer, object created in InterfaceUnit
Definition: GraphicUnit.cpp:50
TTrainOperatingData
Data for a specific train for use during operation.
Definition: TrainUnit.h:162
FailCreatePoints
@ FailCreatePoints
Definition: TrainUnit.h:40
TRailGraphics::smBlack
Graphics::TBitmap * smBlack
Definition: GraphicUnit.h:876
Bridge
@ Bridge
Definition: TrackUnit.h:67
TRailGraphics::Code_p
Graphics::TBitmap * Code_p
Definition: GraphicUnit.h:943
TTrainFormattedInformation::NumberOfTrains
int NumberOfTrains
number of repeats + 1
Definition: TrainUnit.h:259
TTrain::LagExitPos
int LagExitPos
TrackVector positions, & entry & exit connection positions for the elements that the train occupies.
Definition: TrainUnit.h:337
TTimetableSequenceType
TTimetableSequenceType
Definition: TrainUnit.h:74
TTrain::TimetableMaxRunningSpeed
double TimetableMaxRunningSpeed
the maximum train running speed when in timetable mode (see int SignallerMaxSpeed for signaller contr...
Definition: TrainUnit.h:393
SequTypeForRepeatEntry
@ SequTypeForRepeatEntry
Definition: TrainUnit.h:75
TRailGraphics::bmName
Graphics::TBitmap * bmName
Definition: GraphicUnit.h:524
clB0G0R0
#define clB0G0R0
Definition: GraphicUnit.h:36
Buffers
@ Buffers
Definition: TrackUnit.h:67
TActionVectorEntry::RearStartOrRepeatMins
int RearStartOrRepeatMins
Definition: TrainUnit.h:113
clFrontCodeTimetable
#define clFrontCodeTimetable
Definition: GraphicUnit.h:296
TActionVectorEntry::OtherHeadCode
AnsiString OtherHeadCode
Definition: TrainUnit.h:103